mirror of
https://github.com/golang/go
synced 2024-11-25 01:57:56 -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
|
||||
Struct string // name of the struct type containing the field
|
||||
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 {
|
||||
if e.Err != nil {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
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 value of type " + e.Type.String()
|
||||
}
|
||||
|
||||
func (e *UnmarshalTypeError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// An UnmarshalFieldError describes a JSON object key that
|
||||
// led to an unexported (and therefore unwritable) struct field.
|
||||
//
|
||||
@ -508,7 +517,15 @@ func (d *decodeState) array(v reflect.Value) error {
|
||||
if u != nil {
|
||||
start := d.readIndex()
|
||||
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 {
|
||||
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 {
|
||||
start := d.readIndex()
|
||||
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 {
|
||||
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
|
||||
u, ut, pv := indirect(v, isNull)
|
||||
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 item[0] != '"' {
|
||||
@ -886,7 +933,13 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
|
||||
}
|
||||
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
|
||||
|
@ -77,6 +77,18 @@ type TOuter struct {
|
||||
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
|
||||
// without UseNumber
|
||||
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: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
|
||||
{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": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 8, "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", 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), 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: `{"T": {"X": 23}}`, ptr: new(TOuter), out: TOuter{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 0, "TOuter", "T.X"}},
|
||||
{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", 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: 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: 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
|
||||
{CaseName: Name(""), in: "\n true ", ptr: new(bool), out: true},
|
||||
|
Loading…
Reference in New Issue
Block a user