When GOEXPERIMENT=jsonv2
, json.UnmarshalTypeError
errors have gratuitous error text changes. While we don't guarantee stability of error text, json/v2 goes to some effort to keep errors stable where possible to minimize the impact on existing code and we probably can do better here.
// v1: json: cannot unmarshal number into Go value of type chan int
// v2: json: cannot unmarshal into Go type chan int
var v chan int
text := `1`
err := json.Unmarshal([]byte(text), &v)
// v1: json: cannot unmarshal number into Go value of type error
// v2: json: cannot unmarshal into Go type error: cannot derive concrete type for nil interface with finite type set
var v error
text := `1`
err := json.Unmarshal([]byte(text), &v)
// v1: json: cannot unmarshal string into Go struct field .F.V of type int
// v2: json: cannot unmarshal JSON string into T.F.V of Go type int
type T struct{ F struct{ V int } }
var v T
text := `{"F":{"V":"s"}}`
err := json.Unmarshal([]byte(text), &v)
Comment From: gabyhelp
Related Issues
- encoding/json: nested unmarshaling mangles errors #68750 (closed)
- encoding/json: UnmarshalTypeError.Error message seems insensible #43126
- encoding/json: wrong type name used in UnmarshalTypeError #68941 (closed)
- proposal: encoding/json: wrap error from TextUnmarshaler/Unmarshaler to locate the problem in input #58655
- encoding/json: UnmarshalTypeError is clobbered when using nested custom marshallers #61337 (closed)
- proposal: encoding/json: include field name in unmarshal error messages for types that implement `UnmarshalJSON` #71958 (closed)
- encoding/json: confusing errors when unmarshaling custom types #28189
- encoding/json: unmarshal regression under goexperiment.jsonv2 #74614
- Go json unmarshal silent errors #46064 (closed)
Related Code Changes
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: gopherbot
Change https://go.dev/cl/689918 mentions this issue: encoding/json: reduce error text regressions under goexperiment.jsonv2
Comment From: dsnet
Okay, while my CL will improve the error text, it doesn't "fix" the reporting of struct fields, which has always been broken in v1 and I don't feel motivated to replicate the broken behavior in v2.
type Nested struct{ F2 int }
type RootNamed struct{ F1 Nested }
type RootUnnamed struct{ F1 struct{ F2 int } }
fmt.Println(json.Unmarshal([]byte(`{"F1":true}`), new(RootNamed)))
fmt.Println(json.Unmarshal([]byte(`{"F1":{"F2":true}}`), new(RootNamed)))
fmt.Println(json.Unmarshal([]byte(`{"F1":true}`), new(RootUnnamed)))
fmt.Println(json.Unmarshal([]byte(`{"F1":{"F2":true}}`), new(RootUnnamed)))
This prints:
json: cannot unmarshal bool into Go struct field RootNamed.F1 of type main.Nested
json: cannot unmarshal bool into Go struct field Nested.F1.F2 of type int
json: cannot unmarshal bool into Go struct field RootUnnamed.F1 of type struct { F2 int }
json: cannot unmarshal bool into Go struct field .F1.F2 of type int
Notice that the "root" struct type that it uses is the struct type immediately preceding the last field.
Thus, you end up with silly outputs like Nested.F1.F2
which doesn't make sense since there is no F1
field in the Nested
type.
Comment From: dsnet
Oh fun, I reported this issue 5 years ago in #43126. 😅