diff options
| author | Jirka Daněk <dnk@mail.muni.cz> | 2016-01-18 16:26:05 +0100 |
|---|---|---|
| committer | Russ Cox <rsc@golang.org> | 2016-10-05 20:28:59 +0000 |
| commit | b9fd510cd00b6aa26e2ea7001a07b90ebf97d2ed (patch) | |
| tree | 6b321a11850520fbb60d0f6048e3ef24808d2850 /src/encoding/json/decode.go | |
| parent | cb986def671cd244e4682a476cff51c4cff2d8f8 (diff) | |
| download | go-b9fd510cd00b6aa26e2ea7001a07b90ebf97d2ed.tar.xz | |
encoding/json: add struct and field name to UnmarshalTypeError message
The UnmarshalTypeError has two new fields Struct and Field,
used when constructing the error message.
Fixes #6716.
Change-Id: I67da171480a9491960b3ae81893770644180f848
Reviewed-on: https://go-review.googlesource.com/18692
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/encoding/json/decode.go')
| -rw-r--r-- | src/encoding/json/decode.go | 82 |
1 files changed, 55 insertions, 27 deletions
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index 2eda875bfd..ceaecec67c 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -112,9 +112,14 @@ type UnmarshalTypeError struct { Value string // description of JSON value - "bool", "array", "number -5" Type reflect.Type // type of Go value it could not be assigned to Offset int64 // error occurred after reading Offset bytes + Struct string // name of the struct type containing the field + Field string // name of the field holding the Go value } func (e *UnmarshalTypeError) Error() string { + 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() } @@ -248,10 +253,14 @@ func isValidNumber(s string) bool { // decodeState represents the state while decoding a JSON value. type decodeState struct { - data []byte - off int // read offset in data - scan scanner - nextscan scanner // for calls to nextValue + data []byte + off int // read offset in data + scan scanner + nextscan scanner // for calls to nextValue + errorContext struct { // provides context for type errors + Struct string + Field string + } savedError error useNumber bool } @@ -265,22 +274,37 @@ func (d *decodeState) init(data []byte) *decodeState { d.data = data d.off = 0 d.savedError = nil + d.errorContext.Struct = "" + d.errorContext.Field = "" return d } // error aborts the decoding by panicking with err. func (d *decodeState) error(err error) { - panic(err) + panic(d.addErrorContext(err)) } // saveError saves the first err it is called with, // for reporting at the end of the unmarshal. func (d *decodeState) saveError(err error) { if d.savedError == nil { - d.savedError = err + d.savedError = d.addErrorContext(err) } } +// addErrorContext returns a new error enhanced with information from d.errorContext +func (d *decodeState) addErrorContext(err error) error { + if d.errorContext.Struct != "" || d.errorContext.Field != "" { + switch err := err.(type) { + case *UnmarshalTypeError: + err.Struct = d.errorContext.Struct + err.Field = d.errorContext.Field + return err + } + } + return err +} + // next cuts off and returns the next full JSON value in d.data[d.off:]. // The next value is known to be an object or array, not a literal. func (d *decodeState) next() []byte { @@ -457,7 +481,7 @@ func (d *decodeState) array(v reflect.Value) { return } if ut != nil { - d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)}) d.off-- d.next() return @@ -476,7 +500,7 @@ func (d *decodeState) array(v reflect.Value) { // Otherwise it's invalid. fallthrough default: - d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)}) d.off-- d.next() return @@ -566,7 +590,7 @@ func (d *decodeState) object(v reflect.Value) { return } if ut != nil { - d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)}) d.off-- d.next() // skip over { } in input return @@ -594,7 +618,7 @@ func (d *decodeState) object(v reflect.Value) { reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: default: if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { - d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)}) d.off-- d.next() // skip over { } in input return @@ -604,9 +628,9 @@ func (d *decodeState) object(v reflect.Value) { v.Set(reflect.MakeMap(t)) } case reflect.Struct: - + // ok default: - d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)}) d.off-- d.next() // skip over { } in input return @@ -671,6 +695,8 @@ func (d *decodeState) object(v reflect.Value) { } subv = subv.Field(i) } + d.errorContext.Field = f.name + d.errorContext.Struct = v.Type().Name() } } @@ -682,7 +708,6 @@ func (d *decodeState) object(v reflect.Value) { d.error(errPhase) } - // Read value. if destring { switch qv := d.valueQuoted().(type) { case nil: @@ -714,7 +739,7 @@ func (d *decodeState) object(v reflect.Value) { s := string(key) n, err := strconv.ParseInt(s, 10, 64) if err != nil || reflect.Zero(kt).OverflowInt(n) { - d.saveError(&UnmarshalTypeError{"number " + s, kt, int64(start + 1)}) + d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)}) return } kv = reflect.ValueOf(n).Convert(kt) @@ -722,7 +747,7 @@ func (d *decodeState) object(v reflect.Value) { s := string(key) n, err := strconv.ParseUint(s, 10, 64) if err != nil || reflect.Zero(kt).OverflowUint(n) { - d.saveError(&UnmarshalTypeError{"number " + s, kt, int64(start + 1)}) + d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)}) return } kv = reflect.ValueOf(n).Convert(kt) @@ -741,6 +766,9 @@ func (d *decodeState) object(v reflect.Value) { if op != scanObjectValue { d.error(errPhase) } + + d.errorContext.Struct = "" + d.errorContext.Field = "" } } @@ -767,7 +795,7 @@ func (d *decodeState) convertNumber(s string) (interface{}, error) { } f, err := strconv.ParseFloat(s, 64) if err != nil { - return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} + return nil, &UnmarshalTypeError{Value: "number " + s, Type: reflect.TypeOf(0.0), Offset: int64(d.off)} } return f, nil } @@ -800,7 +828,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool if fromQuoted { d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) } else { - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)}) } return } @@ -835,7 +863,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool if fromQuoted { d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) } else { - d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off)}) } case reflect.Bool: v.SetBool(value) @@ -843,7 +871,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool if v.NumMethod() == 0 { v.Set(reflect.ValueOf(value)) } else { - d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off)}) } } @@ -858,10 +886,10 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool } switch v.Kind() { default: - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)}) case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)}) break } b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) @@ -877,7 +905,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool if v.NumMethod() == 0 { v.Set(reflect.ValueOf(string(s))) } else { - d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)}) } } @@ -902,7 +930,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool if fromQuoted { d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) } else { - d.error(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) + d.error(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off)}) } case reflect.Interface: n, err := d.convertNumber(s) @@ -911,7 +939,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool break } if v.NumMethod() != 0 { - d.saveError(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off)}) break } v.Set(reflect.ValueOf(n)) @@ -919,7 +947,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: n, err := strconv.ParseInt(s, 10, 64) if err != nil || v.OverflowInt(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)}) break } v.SetInt(n) @@ -927,7 +955,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: n, err := strconv.ParseUint(s, 10, 64) if err != nil || v.OverflowUint(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)}) break } v.SetUint(n) @@ -935,7 +963,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.Float32, reflect.Float64: n, err := strconv.ParseFloat(s, v.Type().Bits()) if err != nil || v.OverflowFloat(n) { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)}) break } v.SetFloat(n) |
