J **int
}
+type NestedUnamed struct{ F struct{ V int } }
+
var unmarshalTests = []struct {
CaseName
in string
F string `json:"-,omitempty"`
}{"hello"},
},
+
+ {
+ CaseName: Name("ErrorForNestedUnamed"),
+ in: `{"F":{"V":"s"}}`,
+ ptr: new(NestedUnamed),
+ out: NestedUnamed{},
+ err: &UnmarshalTypeError{Value: "string", Type: reflect.TypeFor[int](), Offset: 13, Field: "F.V"},
+ },
+ {
+ CaseName: Name("ErrorInterface"),
+ in: `1`,
+ ptr: new(error),
+ out: error(nil),
+ err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[error](), Offset: 1},
+ },
+ {
+ CaseName: Name("ErrorChan"),
+ in: `1`,
+ ptr: new(chan int),
+ out: (chan int)(nil),
+ err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[chan int](), Offset: 1},
+ },
}
func TestMarshal(t *testing.T) {
var (
ErrCycle = errors.New("encountered a cycle")
ErrNonNilReference = errors.New("value must be passed as a non-nil pointer reference")
+ ErrNilInterface = errors.New("cannot derive concrete type for nil interface with finite type set")
)
var (
return &fncs
}
-var errNilInterface = errors.New("cannot derive concrete type for nil interface with finite type set")
-
func makeInterfaceArshaler(t reflect.Type) *arshaler {
// NOTE: Values retrieved from an interface are not addressable,
// so we shallow copy the values to make them addressable and
k := dec.PeekKind()
if !isAnyType(t) {
- return newUnmarshalErrorBeforeWithSkipping(dec, uo, t, errNilInterface)
+ return newUnmarshalErrorBeforeWithSkipping(dec, uo, t, internal.ErrNilInterface)
}
switch k {
case 'f', 't':
inBuf: `"hello"`,
inVal: new(io.Reader),
want: new(io.Reader),
- wantErr: EU(errNilInterface).withType(0, T[io.Reader]()),
+ wantErr: EU(internal.ErrNilInterface).withType(0, T[io.Reader]()),
}, {
name: jsontest.Name("Interfaces/Empty/False"),
inBuf: `false`,
inBuf: `{"X":"hello"}`,
inVal: addr(struct{ X fmt.Stringer }{nil}),
want: addr(struct{ X fmt.Stringer }{nil}),
- wantErr: EU(errNilInterface).withPos(`{"X":`, "/X").withType(0, T[fmt.Stringer]()),
+ wantErr: EU(internal.ErrNilInterface).withPos(`{"X":`, "/X").withType(0, T[fmt.Stringer]()),
}, {
name: jsontest.Name("Functions/Interface/NetIP"),
opts: []Options{
// is positioned right before the next token or value, which causes an error.
// It does not record the next JSON kind as this error is used to indicate
// the receiving Go value is invalid to unmarshal into (and not a JSON error).
+// However, if [jsonflags.ReportErrorsWithLegacySemantics] is specified,
+// then it does record the next JSON kind for historical reporting reasons.
func newUnmarshalErrorBefore(d *jsontext.Decoder, t reflect.Type, err error) error {
+ var k jsontext.Kind
+ if export.Decoder(d).Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
+ k = d.PeekKind()
+ }
return &SemanticError{action: "unmarshal", GoType: t, Err: err,
ByteOffset: d.InputOffset() + int64(export.Decoder(d).CountNextDelimWhitespace()),
- JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, +1))}
+ JSONPointer: jsontext.Pointer(export.Decoder(d).AppendStackPointer(nil, +1)),
+ JSONKind: k}
}
// newUnmarshalErrorBeforeWithSkipping is like [newUnmarshalErrorBefore],
}
func (e *UnmarshalTypeError) Error() string {
- s := "json: cannot unmarshal"
- if e.Value != "" {
- s += " JSON " + e.Value
- }
- s += " into"
- var preposition string
- if e.Field != "" {
- s += " " + e.Struct + "." + e.Field
- preposition = " of"
- }
- if e.Type != nil {
- s += preposition
- s += " Go type " + e.Type.String()
+ var s string
+ if e.Struct != "" || e.Field != "" {
+ s = "json: cannot unmarshal " + e.Value + " into Go struct field " + e.Struct + "." + e.Field + " of type " + e.Type.String()
+ } else {
+ s = "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}
if e.Err != nil {
s += ": " + e.Err.Error()
J **int
}
+type NestedUnamed struct{ F struct{ V int } }
+
var unmarshalTests = []struct {
CaseName
in string
F string `json:"-,omitempty"`
}{"hello"},
},
+
+ {
+ CaseName: Name("ErrorForNestedUnamed"),
+ in: `{"F":{"V":"s"}}`,
+ ptr: new(NestedUnamed),
+ out: NestedUnamed{},
+ err: &UnmarshalTypeError{Value: "string", Type: reflect.TypeFor[int](), Offset: 10, Struct: "NestedUnamed", Field: "F.V"},
+ },
+ {
+ CaseName: Name("ErrorInterface"),
+ in: `1`,
+ ptr: new(error),
+ out: error(nil),
+ err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[error]()},
+ },
+ {
+ CaseName: Name("ErrorChan"),
+ in: `1`,
+ ptr: new(chan int),
+ out: (chan int)(nil),
+ err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[chan int]()},
+ },
}
func TestMarshal(t *testing.T) {
CaseName
in, err string
}{
- {Name(""), `{"result":"x"}`, `json: cannot unmarshal JSON string into WrongString.result of Go type string: invalid character 'x' looking for beginning of object key string`},
- {Name(""), `{"result":"foo"}`, `json: cannot unmarshal JSON string into WrongString.result of Go type string: invalid character 'f' looking for beginning of object key string`},
- {Name(""), `{"result":"123"}`, `json: cannot unmarshal JSON string into WrongString.result of Go type string: invalid character '1' looking for beginning of object key string`},
- {Name(""), `{"result":123}`, `json: cannot unmarshal JSON number into WrongString.result of Go type string`},
- {Name(""), `{"result":"\""}`, `json: cannot unmarshal JSON string into WrongString.result of Go type string: unexpected end of JSON input`},
- {Name(""), `{"result":"\"foo"}`, `json: cannot unmarshal JSON string into WrongString.result of Go type string: unexpected end of JSON input`},
+ {Name(""), `{"result":"x"}`, `json: cannot unmarshal string into Go struct field WrongString.result of type string: invalid character 'x' looking for beginning of object key string`},
+ {Name(""), `{"result":"foo"}`, `json: cannot unmarshal string into Go struct field WrongString.result of type string: invalid character 'f' looking for beginning of object key string`},
+ {Name(""), `{"result":"123"}`, `json: cannot unmarshal string into Go struct field WrongString.result of type string: invalid character '1' looking for beginning of object key string`},
+ {Name(""), `{"result":123}`, `json: cannot unmarshal number into Go struct field WrongString.result of type string`},
+ {Name(""), `{"result":"\""}`, `json: cannot unmarshal string into Go struct field WrongString.result of type string: unexpected end of JSON input`},
+ {Name(""), `{"result":"\"foo"}`, `json: cannot unmarshal string into Go struct field WrongString.result of type string: unexpected end of JSON input`},
}
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
ptr: new(S1),
out: &S1{R: 2},
err: &UnmarshalTypeError{
+ Value: "number",
Type: reflect.TypeFor[S1](),
Offset: len64(`{"R":2,"Q":`),
Struct: "S1",
ptr: new(S5),
out: &S5{R: 2},
err: &UnmarshalTypeError{
+ Value: "number",
Type: reflect.TypeFor[S5](),
Offset: len64(`{"R":2,"Q":`),
Struct: "S5",
if err.Err == jsonv2.ErrUnknownName {
return fmt.Errorf("json: unknown field %q", err.JSONPointer.LastToken())
}
+ if err.Err == internal.ErrNilInterface {
+ err.Err = nil // non-descriptive for historical reasons
+ }
// Historically, UnmarshalTypeError has always been inconsistent
// about how it reported position information.