mirror of
https://github.com/golang/go
synced 2024-11-25 09:17:57 -07:00
encoding/json: return JSON field with all marshal errors
This commit is contained in:
parent
252e9def65
commit
ac6e99f737
@ -128,15 +128,24 @@ type UnmarshalTypeError struct {
|
|||||||
Offset int64 // error occurred after reading Offset bytes
|
Offset int64 // error occurred after reading Offset bytes
|
||||||
Struct string // name of the struct type containing the field
|
Struct string // name of the struct type containing the field
|
||||||
Field string // the full path from root node to the field, include embedded struct
|
Field string // the full path from root node to the field, include embedded struct
|
||||||
|
Err error // Error which occurred during unmarshal
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *UnmarshalTypeError) Error() string {
|
func (e *UnmarshalTypeError) Error() string {
|
||||||
|
if e.Err != nil {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
if e.Struct != "" || e.Field != "" {
|
if e.Struct != "" || e.Field != "" {
|
||||||
return "json: cannot unmarshal " + e.Value + " into Go struct field " + e.Struct + "." + e.Field + " of type " + e.Type.String()
|
return "json: cannot unmarshal " + e.Value + " into Go struct field " + e.Struct + "." + e.Field + " of type " + e.Type.String()
|
||||||
}
|
}
|
||||||
return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
|
return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *UnmarshalTypeError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
// An UnmarshalFieldError describes a JSON object key that
|
// An UnmarshalFieldError describes a JSON object key that
|
||||||
// led to an unexported (and therefore unwritable) struct field.
|
// led to an unexported (and therefore unwritable) struct field.
|
||||||
//
|
//
|
||||||
@ -508,7 +517,15 @@ func (d *decodeState) array(v reflect.Value) error {
|
|||||||
if u != nil {
|
if u != nil {
|
||||||
start := d.readIndex()
|
start := d.readIndex()
|
||||||
d.skip()
|
d.skip()
|
||||||
return u.UnmarshalJSON(d.data[start:d.off])
|
if err := u.UnmarshalJSON(d.data[start:d.off]); err != nil {
|
||||||
|
if e, ok := err.(*UnmarshalTypeError); ok {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off), Err: err})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if ut != nil {
|
if ut != nil {
|
||||||
d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
|
d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
|
||||||
@ -605,7 +622,16 @@ func (d *decodeState) object(v reflect.Value) error {
|
|||||||
if u != nil {
|
if u != nil {
|
||||||
start := d.readIndex()
|
start := d.readIndex()
|
||||||
d.skip()
|
d.skip()
|
||||||
return u.UnmarshalJSON(d.data[start:d.off])
|
err := u.UnmarshalJSON(d.data[start:d.off])
|
||||||
|
if err != nil {
|
||||||
|
if e, ok := err.(*UnmarshalTypeError); ok {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off), Err: err})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if ut != nil {
|
if ut != nil {
|
||||||
d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
|
d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
|
||||||
@ -861,7 +887,28 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
|
|||||||
isNull := item[0] == 'n' // null
|
isNull := item[0] == 'n' // null
|
||||||
u, ut, pv := indirect(v, isNull)
|
u, ut, pv := indirect(v, isNull)
|
||||||
if u != nil {
|
if u != nil {
|
||||||
return u.UnmarshalJSON(item)
|
err := u.UnmarshalJSON(item)
|
||||||
|
if err != nil {
|
||||||
|
if e, ok := err.(*UnmarshalTypeError); ok {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var val string
|
||||||
|
switch item[0] {
|
||||||
|
case '"':
|
||||||
|
val = "string"
|
||||||
|
case 'n':
|
||||||
|
val = "null"
|
||||||
|
case 't', 'f':
|
||||||
|
val = "bool"
|
||||||
|
default:
|
||||||
|
val = "number"
|
||||||
|
}
|
||||||
|
|
||||||
|
d.saveError(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.readIndex()), Err: err})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if ut != nil {
|
if ut != nil {
|
||||||
if item[0] != '"' {
|
if item[0] != '"' {
|
||||||
@ -886,7 +933,13 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
|
|||||||
}
|
}
|
||||||
panic(phasePanicMsg)
|
panic(phasePanicMsg)
|
||||||
}
|
}
|
||||||
return ut.UnmarshalText(s)
|
|
||||||
|
err := ut.UnmarshalText(s)
|
||||||
|
if err != nil {
|
||||||
|
d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.readIndex()), Err: err})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
v = pv
|
v = pv
|
||||||
|
@ -77,6 +77,18 @@ type TOuter struct {
|
|||||||
T TAlias
|
T TAlias
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EV struct {
|
||||||
|
EVS EVS
|
||||||
|
}
|
||||||
|
|
||||||
|
type EVS string
|
||||||
|
|
||||||
|
var EVerr = errors.New("error")
|
||||||
|
|
||||||
|
func (*EVS) UnmarshalJSON(_ []byte) error {
|
||||||
|
return EVerr
|
||||||
|
}
|
||||||
|
|
||||||
// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and
|
// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and
|
||||||
// without UseNumber
|
// without UseNumber
|
||||||
var ifaceNumAsFloat64 = map[string]any{
|
var ifaceNumAsFloat64 = map[string]any{
|
||||||
@ -437,17 +449,18 @@ var unmarshalTests = []struct {
|
|||||||
{CaseName: Name(""), in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},
|
{CaseName: Name(""), in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},
|
||||||
{CaseName: Name(""), in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
|
{CaseName: Name(""), in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
|
||||||
{CaseName: Name(""), in: "null", ptr: new(any), out: nil},
|
{CaseName: Name(""), in: "null", ptr: new(any), out: nil},
|
||||||
{CaseName: Name(""), in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeFor[string](), 7, "T", "X"}},
|
{CaseName: Name(""), in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeFor[string](), 7, "T", "X", nil}},
|
||||||
{CaseName: Name(""), in: `{"X": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 8, "T", "X"}},
|
{CaseName: Name(""), in: `{"X": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 8, "T", "X", nil}},
|
||||||
{CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), out: tx{}},
|
{CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), out: tx{}},
|
||||||
{CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), out: tx{}},
|
{CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), out: tx{}},
|
||||||
{CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), err: fmt.Errorf("json: unknown field \"x\""), disallowUnknownFields: true},
|
{CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), err: fmt.Errorf("json: unknown field \"x\""), disallowUnknownFields: true},
|
||||||
{CaseName: Name(""), in: `{"S": 23}`, ptr: new(W), out: W{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[SS](), 0, "W", "S"}},
|
{CaseName: Name(""), in: `{"S": 23}`, ptr: new(W), out: W{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[SS](), 0, "W", "S", nil}},
|
||||||
{CaseName: Name(""), in: `{"T": {"X": 23}}`, ptr: new(TOuter), out: TOuter{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 0, "TOuter", "T.X"}},
|
{CaseName: Name(""), in: `{"T": {"X": 23}}`, ptr: new(TOuter), out: TOuter{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 0, "TOuter", "T.X", nil}},
|
||||||
{CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
|
{CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
|
||||||
{CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
|
{CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
|
||||||
{CaseName: Name(""), in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsFloat64},
|
{CaseName: Name(""), in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsFloat64},
|
||||||
{CaseName: Name(""), in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsNumber, useNumber: true},
|
{CaseName: Name(""), in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsNumber, useNumber: true},
|
||||||
|
{CaseName: Name(""), in: `{"EVS": 23}`, ptr: new(EV), out: EV{}, err: &UnmarshalTypeError{"string", reflect.TypeFor[EVS](), 0, "EV", "EVS", EVerr}},
|
||||||
|
|
||||||
// raw values with whitespace
|
// raw values with whitespace
|
||||||
{CaseName: Name(""), in: "\n true ", ptr: new(bool), out: true},
|
{CaseName: Name(""), in: "\n true ", ptr: new(bool), out: true},
|
||||||
|
Loading…
Reference in New Issue
Block a user