encoding/json: fix Unmarshal of value assigned to an interface variable.
When I assign a variable to an interface variable,
the json.Unmarshal will convert it to map[string]any instead of
filling out the underlying variable.
E.g.
var p string
var i any = p
_ = json.Unmarshal([]byte(`{"x":1}`), &i)
fmt.Println(reflect.TypeOf(i))
This will print map[string]interface{}
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index 1a05ef5..37aed4d 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -613,15 +613,26 @@
return nil
}
v = pv
- t := v.Type()
-
// Decoding into nil interface? Switch to non-reflect code.
- if v.Kind() == reflect.Interface && v.NumMethod() == 0 {
- oi := d.objectInterface()
- v.Set(reflect.ValueOf(oi))
- return nil
+ // If the interface is not nil, replace it with the underlying value
+ // and set the interface with that value in the end.
+ var iReplace bool
+ if v.Kind() == reflect.Interface {
+ if v.IsNil() {
+ if v.NumMethod() == 0 {
+ oi := d.objectInterface()
+ v.Set(reflect.ValueOf(oi))
+ return nil
+ }
+ } else {
+ el := v.Elem()
+ v = reflect.New(el.Type()).Elem()
+ iReplace = true
+ }
}
+ t := v.Type()
+
var fields structFields
// Check type of target:
@@ -828,6 +839,9 @@
panic(phasePanicMsg)
}
}
+ if iReplace {
+ pv.Set(v)
+ }
return nil
}
diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go
index 71895a9..c82e17c 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -1271,6 +1271,27 @@
}
}
+func TestUnmarshalAssignToInterface(t *testing.T) {
+ type Z struct {
+ Y int
+ }
+ var u Z
+ var i any = u
+ err := Unmarshal([]byte(`{"Y":1}`), &i)
+ if err != nil {
+ panic(err)
+ }
+ u, ok := i.(Z)
+ if !ok {
+ t.Errorf("wrong type of value, expected U, got=%s", reflect.TypeOf(i))
+ } else {
+ if u.Y != 1 {
+ t.Errorf("failed to unmarshal")
+ }
+ }
+
+}
+
// Independent of Decode, basic coverage of the accessors in Number
func TestNumberAccessors(t *testing.T) {
tests := []struct {
@@ -2497,9 +2518,9 @@
var v any
v = &v
data := []byte(`{"a": "b"}`)
-
- if err := Unmarshal(data, v); err != nil {
- t.Fatalf("Unmarshal error: %v", err)
+ err := Unmarshal(data, v)
+ if err == nil {
+ t.Fatalf("unexpected error: %v", err)
}
}
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
This is a known issue, and unfortunately I don't think we can change this behavior in v1.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
This is a known issue, and unfortunately I don't think we can change this behavior in v1.
I don't think anyone would use an interface variable assigned to a struct instance so that the variable would only be transformed into a map after Unmarshal. But otherwise I don't see any break of compatibility.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Dennis GlossThis is a known issue, and unfortunately I don't think we can change this behavior in v1.
I don't think anyone would use an interface variable assigned to a struct instance so that the variable would only be transformed into a map after Unmarshal. But otherwise I don't see any break of compatibility.
As Alan Donovan pointed out, people reuse the `any` variable to unmarshal different types of json i.e. map, array, string. So I kept the old behavior for `any` type variables.
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |