-
Notifications
You must be signed in to change notification settings - Fork 18k
encoding/json: json.Unmarshal converts an interface variable with a value to a Map #69875
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Change https://go.dev/cl/619995 mentions this issue: |
I think this is just a misunderstanding in the logic surrounding the call to Unmarshal. If you pass Unmarshal a https://go.dev/play/p/-1ceojzwcMT type S struct {
X int
}
var s S
var i any = reflect.ValueOf(&s).Interface()
_ = json.Unmarshal([]byte(`{"X":1}`), i)
fmt.Println(reflect.TypeOf(i)) // "*main.S" |
Duplicate of #26946 |
@adonovan Thank you, that's a useful workaround having the interface variable assigned to a pointer. I do still think it's an issue thought, that Unmarshal can't handle the pointer of the interface variable. |
@seankhliao sorry, I missed that, I will add the older issue number to the fix |
Unmarshal is handling the pointer to the interface variable exactly as documented. If you want it to populate a struct variable, you need to give it a pointer to a struct variable. |
@adonovan Well, the problem is that I expected it to be unmarshaled into an underlying struct because as far as I understand an interface is not a value, it's more like a constraint on a struct. So unmarshaling into an empty interface doesn't make much sense to me. |
An interface type is indeed a kind of constraint: it's the subset of all types that have a particular set of methods. But an interface value is a value capable of representing a variety of (or perhaps |
@adonovan Yes, that's the use case for the empty |
Personally I think the current behavior is fine. Much more importantly, we can't change the current behavior. It would certainly break existing working programs. |
Unmarshal wants a pointer to a variable, which it will update. The type of that variable determines what representation Unmarshal will use. Because Unmarshal long preceded generics, the only way for it to say "I need a pointer to something" is to have a parameter of type |
@ianlancetaylor It would only break programs which assign non- |
Unmarshal has always clobbered the contents of the variable, making it independent of whatever junk happened to be in it. Breaking that invariant would certainly break existing programs.
The error returned by Unmarshal explains the problem: "json: cannot unmarshal object into Go value of type main.I". This program is another example of "holding it wrong" by passing Unmarshal an interface variable that it cannot possibly update with any simple JSON value. |
@adonovan I'm confused, there's no error in the example |
Unmarshal returns an error, but your example code ignores it. Here's what happens if you report it: https://go.dev/play/p/Mhi3Xlasdmd. |
@adonovan got it, sorry, missing error handling, my fix would unmarshal it though. |
Go version
go1.23.2 darwin/arm64
Output of
go env
in your module/workspace:What did you do?
I was working with reflect package and wanted to json.Unmarshal a value received from
reflect.Value#Interface()
. Because theInterface
method returns anyjson.Unmarshal
automatically converts the passed value into amap[string]any
.What did you see happen?
It can be reproduced with this code
It happens because of the
reflect.Value#Elem()
function which unwraps the pointer but does not unwrap the interface and you have to call theElem()
method two times to get the valuee.g.
At first, I fixed the
Elem()
behaviour but too many packages depended on the double unwrapping. So I worked onjson.Unmarshal
and here's my fixI believe the conversion to a map exists for the
any
interface withnil
value.What did you expect to see?
I want the underlying value of the interface to be updated, instead of reassigning the interface variable.
I'll be happy to make any changes to my fix
The text was updated successfully, but these errors were encountered: