aboutsummaryrefslogtreecommitdiff
path: root/src/encoding
diff options
context:
space:
mode:
Diffstat (limited to 'src/encoding')
-rw-r--r--src/encoding/json/decode_test.go24
-rw-r--r--src/encoding/json/internal/internal.go1
-rw-r--r--src/encoding/json/v2/arshal_default.go4
-rw-r--r--src/encoding/json/v2/arshal_test.go4
-rw-r--r--src/encoding/json/v2/errors.go9
-rw-r--r--src/encoding/json/v2_decode.go18
-rw-r--r--src/encoding/json/v2_decode_test.go38
-rw-r--r--src/encoding/json/v2_inject.go3
8 files changed, 76 insertions, 25 deletions
diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go
index 0df31c82c8..d12495f90b 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -416,6 +416,8 @@ type DoublePtr struct {
J **int
}
+type NestedUnamed struct{ F struct{ V int } }
+
var unmarshalTests = []struct {
CaseName
in string
@@ -1213,6 +1215,28 @@ var unmarshalTests = []struct {
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) {
diff --git a/src/encoding/json/internal/internal.go b/src/encoding/json/internal/internal.go
index f587c7b32c..c95f83fe44 100644
--- a/src/encoding/json/internal/internal.go
+++ b/src/encoding/json/internal/internal.go
@@ -21,6 +21,7 @@ var AllowInternalUse NotForPublicUse
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 (
diff --git a/src/encoding/json/v2/arshal_default.go b/src/encoding/json/v2/arshal_default.go
index 0b30ac4fb7..f3fc79beac 100644
--- a/src/encoding/json/v2/arshal_default.go
+++ b/src/encoding/json/v2/arshal_default.go
@@ -1690,8 +1690,6 @@ func makePointerArshaler(t reflect.Type) *arshaler {
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
@@ -1797,7 +1795,7 @@ func makeInterfaceArshaler(t reflect.Type) *arshaler {
k := dec.PeekKind()
if !isAnyType(t) {
- return newUnmarshalErrorBeforeWithSkipping(dec, uo, t, errNilInterface)
+ return newUnmarshalErrorBeforeWithSkipping(dec, uo, t, internal.ErrNilInterface)
}
switch k {
case 'f', 't':
diff --git a/src/encoding/json/v2/arshal_test.go b/src/encoding/json/v2/arshal_test.go
index 88887e1b00..f1ee2e2e3a 100644
--- a/src/encoding/json/v2/arshal_test.go
+++ b/src/encoding/json/v2/arshal_test.go
@@ -7496,7 +7496,7 @@ func TestUnmarshal(t *testing.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`,
@@ -8344,7 +8344,7 @@ func TestUnmarshal(t *testing.T) {
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{
diff --git a/src/encoding/json/v2/errors.go b/src/encoding/json/v2/errors.go
index 48cdcc953b..1f31505869 100644
--- a/src/encoding/json/v2/errors.go
+++ b/src/encoding/json/v2/errors.go
@@ -120,10 +120,17 @@ func newMarshalErrorBefore(e *jsontext.Encoder, t reflect.Type, err error) error
// 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],
diff --git a/src/encoding/json/v2_decode.go b/src/encoding/json/v2_decode.go
index c82ee903c3..1041ec7ee4 100644
--- a/src/encoding/json/v2_decode.go
+++ b/src/encoding/json/v2_decode.go
@@ -117,19 +117,11 @@ type UnmarshalTypeError struct {
}
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()
diff --git a/src/encoding/json/v2_decode_test.go b/src/encoding/json/v2_decode_test.go
index 1e4914efc4..f9b0a60f47 100644
--- a/src/encoding/json/v2_decode_test.go
+++ b/src/encoding/json/v2_decode_test.go
@@ -420,6 +420,8 @@ type DoublePtr struct {
J **int
}
+type NestedUnamed struct{ F struct{ V int } }
+
var unmarshalTests = []struct {
CaseName
in string
@@ -1219,6 +1221,28 @@ var unmarshalTests = []struct {
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) {
@@ -1552,12 +1576,12 @@ func TestErrorMessageFromMisusedString(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) {
@@ -2545,6 +2569,7 @@ func TestUnmarshalEmbeddedUnexported(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",
@@ -2577,6 +2602,7 @@ func TestUnmarshalEmbeddedUnexported(t *testing.T) {
ptr: new(S5),
out: &S5{R: 2},
err: &UnmarshalTypeError{
+ Value: "number",
Type: reflect.TypeFor[S5](),
Offset: len64(`{"R":2,"Q":`),
Struct: "S5",
diff --git a/src/encoding/json/v2_inject.go b/src/encoding/json/v2_inject.go
index f903588431..31cdb4d61a 100644
--- a/src/encoding/json/v2_inject.go
+++ b/src/encoding/json/v2_inject.go
@@ -73,6 +73,9 @@ func transformUnmarshalError(root any, err error) error {
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.