Skip to content

Commit b9fd510

Browse files
jiridanekrsc
authored andcommitted
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 <[email protected]> Reviewed-by: Russ Cox <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent cb986de commit b9fd510

File tree

2 files changed

+90
-34
lines changed

2 files changed

+90
-34
lines changed

src/encoding/json/decode.go

+55-27
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,14 @@ type UnmarshalTypeError struct {
112112
Value string // description of JSON value - "bool", "array", "number -5"
113113
Type reflect.Type // type of Go value it could not be assigned to
114114
Offset int64 // error occurred after reading Offset bytes
115+
Struct string // name of the struct type containing the field
116+
Field string // name of the field holding the Go value
115117
}
116118

117119
func (e *UnmarshalTypeError) Error() string {
120+
if e.Struct != "" || e.Field != "" {
121+
return "json: cannot unmarshal " + e.Value + " into Go struct field " + e.Struct + "." + e.Field + " of type " + e.Type.String()
122+
}
118123
return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
119124
}
120125

@@ -248,10 +253,14 @@ func isValidNumber(s string) bool {
248253

249254
// decodeState represents the state while decoding a JSON value.
250255
type decodeState struct {
251-
data []byte
252-
off int // read offset in data
253-
scan scanner
254-
nextscan scanner // for calls to nextValue
256+
data []byte
257+
off int // read offset in data
258+
scan scanner
259+
nextscan scanner // for calls to nextValue
260+
errorContext struct { // provides context for type errors
261+
Struct string
262+
Field string
263+
}
255264
savedError error
256265
useNumber bool
257266
}
@@ -265,22 +274,37 @@ func (d *decodeState) init(data []byte) *decodeState {
265274
d.data = data
266275
d.off = 0
267276
d.savedError = nil
277+
d.errorContext.Struct = ""
278+
d.errorContext.Field = ""
268279
return d
269280
}
270281

271282
// error aborts the decoding by panicking with err.
272283
func (d *decodeState) error(err error) {
273-
panic(err)
284+
panic(d.addErrorContext(err))
274285
}
275286

276287
// saveError saves the first err it is called with,
277288
// for reporting at the end of the unmarshal.
278289
func (d *decodeState) saveError(err error) {
279290
if d.savedError == nil {
280-
d.savedError = err
291+
d.savedError = d.addErrorContext(err)
281292
}
282293
}
283294

295+
// addErrorContext returns a new error enhanced with information from d.errorContext
296+
func (d *decodeState) addErrorContext(err error) error {
297+
if d.errorContext.Struct != "" || d.errorContext.Field != "" {
298+
switch err := err.(type) {
299+
case *UnmarshalTypeError:
300+
err.Struct = d.errorContext.Struct
301+
err.Field = d.errorContext.Field
302+
return err
303+
}
304+
}
305+
return err
306+
}
307+
284308
// next cuts off and returns the next full JSON value in d.data[d.off:].
285309
// The next value is known to be an object or array, not a literal.
286310
func (d *decodeState) next() []byte {
@@ -457,7 +481,7 @@ func (d *decodeState) array(v reflect.Value) {
457481
return
458482
}
459483
if ut != nil {
460-
d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)})
484+
d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
461485
d.off--
462486
d.next()
463487
return
@@ -476,7 +500,7 @@ func (d *decodeState) array(v reflect.Value) {
476500
// Otherwise it's invalid.
477501
fallthrough
478502
default:
479-
d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)})
503+
d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
480504
d.off--
481505
d.next()
482506
return
@@ -566,7 +590,7 @@ func (d *decodeState) object(v reflect.Value) {
566590
return
567591
}
568592
if ut != nil {
569-
d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
593+
d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
570594
d.off--
571595
d.next() // skip over { } in input
572596
return
@@ -594,7 +618,7 @@ func (d *decodeState) object(v reflect.Value) {
594618
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
595619
default:
596620
if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) {
597-
d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
621+
d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
598622
d.off--
599623
d.next() // skip over { } in input
600624
return
@@ -604,9 +628,9 @@ func (d *decodeState) object(v reflect.Value) {
604628
v.Set(reflect.MakeMap(t))
605629
}
606630
case reflect.Struct:
607-
631+
// ok
608632
default:
609-
d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
633+
d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
610634
d.off--
611635
d.next() // skip over { } in input
612636
return
@@ -671,6 +695,8 @@ func (d *decodeState) object(v reflect.Value) {
671695
}
672696
subv = subv.Field(i)
673697
}
698+
d.errorContext.Field = f.name
699+
d.errorContext.Struct = v.Type().Name()
674700
}
675701
}
676702

@@ -682,7 +708,6 @@ func (d *decodeState) object(v reflect.Value) {
682708
d.error(errPhase)
683709
}
684710

685-
// Read value.
686711
if destring {
687712
switch qv := d.valueQuoted().(type) {
688713
case nil:
@@ -714,15 +739,15 @@ func (d *decodeState) object(v reflect.Value) {
714739
s := string(key)
715740
n, err := strconv.ParseInt(s, 10, 64)
716741
if err != nil || reflect.Zero(kt).OverflowInt(n) {
717-
d.saveError(&UnmarshalTypeError{"number " + s, kt, int64(start + 1)})
742+
d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)})
718743
return
719744
}
720745
kv = reflect.ValueOf(n).Convert(kt)
721746
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
722747
s := string(key)
723748
n, err := strconv.ParseUint(s, 10, 64)
724749
if err != nil || reflect.Zero(kt).OverflowUint(n) {
725-
d.saveError(&UnmarshalTypeError{"number " + s, kt, int64(start + 1)})
750+
d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)})
726751
return
727752
}
728753
kv = reflect.ValueOf(n).Convert(kt)
@@ -741,6 +766,9 @@ func (d *decodeState) object(v reflect.Value) {
741766
if op != scanObjectValue {
742767
d.error(errPhase)
743768
}
769+
770+
d.errorContext.Struct = ""
771+
d.errorContext.Field = ""
744772
}
745773
}
746774

@@ -767,7 +795,7 @@ func (d *decodeState) convertNumber(s string) (interface{}, error) {
767795
}
768796
f, err := strconv.ParseFloat(s, 64)
769797
if err != nil {
770-
return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)}
798+
return nil, &UnmarshalTypeError{Value: "number " + s, Type: reflect.TypeOf(0.0), Offset: int64(d.off)}
771799
}
772800
return f, nil
773801
}
@@ -800,7 +828,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
800828
if fromQuoted {
801829
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
802830
} else {
803-
d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
831+
d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)})
804832
}
805833
return
806834
}
@@ -835,15 +863,15 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
835863
if fromQuoted {
836864
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
837865
} else {
838-
d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)})
866+
d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off)})
839867
}
840868
case reflect.Bool:
841869
v.SetBool(value)
842870
case reflect.Interface:
843871
if v.NumMethod() == 0 {
844872
v.Set(reflect.ValueOf(value))
845873
} else {
846-
d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)})
874+
d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off)})
847875
}
848876
}
849877

@@ -858,10 +886,10 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
858886
}
859887
switch v.Kind() {
860888
default:
861-
d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
889+
d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)})
862890
case reflect.Slice:
863891
if v.Type().Elem().Kind() != reflect.Uint8 {
864-
d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
892+
d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)})
865893
break
866894
}
867895
b := make([]byte, base64.StdEncoding.DecodedLen(len(s)))
@@ -877,7 +905,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
877905
if v.NumMethod() == 0 {
878906
v.Set(reflect.ValueOf(string(s)))
879907
} else {
880-
d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
908+
d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)})
881909
}
882910
}
883911

@@ -902,7 +930,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
902930
if fromQuoted {
903931
d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
904932
} else {
905-
d.error(&UnmarshalTypeError{"number", v.Type(), int64(d.off)})
933+
d.error(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off)})
906934
}
907935
case reflect.Interface:
908936
n, err := d.convertNumber(s)
@@ -911,31 +939,31 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
911939
break
912940
}
913941
if v.NumMethod() != 0 {
914-
d.saveError(&UnmarshalTypeError{"number", v.Type(), int64(d.off)})
942+
d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off)})
915943
break
916944
}
917945
v.Set(reflect.ValueOf(n))
918946

919947
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
920948
n, err := strconv.ParseInt(s, 10, 64)
921949
if err != nil || v.OverflowInt(n) {
922-
d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
950+
d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)})
923951
break
924952
}
925953
v.SetInt(n)
926954

927955
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
928956
n, err := strconv.ParseUint(s, 10, 64)
929957
if err != nil || v.OverflowUint(n) {
930-
d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
958+
d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)})
931959
break
932960
}
933961
v.SetUint(n)
934962

935963
case reflect.Float32, reflect.Float64:
936964
n, err := strconv.ParseFloat(s, v.Type().Bits())
937965
if err != nil || v.OverflowFloat(n) {
938-
d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
966+
d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)})
939967
break
940968
}
941969
v.SetFloat(n)

src/encoding/json/decode_test.go

+35-7
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ type V struct {
3333
F1 interface{}
3434
F2 int32
3535
F3 Number
36+
F4 *VOuter
37+
}
38+
39+
type VOuter struct {
40+
V V
3641
}
3742

3843
// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and
@@ -389,7 +394,7 @@ var unmarshalTests = []unmarshalTest{
389394
{in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},
390395
{in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
391396
{in: "null", ptr: new(interface{}), out: nil},
392-
{in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7}},
397+
{in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7, "T", "X"}},
393398
{in: `{"x": 1}`, ptr: new(tx), out: tx{}},
394399
{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
395400
{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
@@ -504,22 +509,22 @@ var unmarshalTests = []unmarshalTest{
504509
{
505510
in: `{"abc":"abc"}`,
506511
ptr: new(map[int]string),
507-
err: &UnmarshalTypeError{"number abc", reflect.TypeOf(0), 2},
512+
err: &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeOf(0), Offset: 2},
508513
},
509514
{
510515
in: `{"256":"abc"}`,
511516
ptr: new(map[uint8]string),
512-
err: &UnmarshalTypeError{"number 256", reflect.TypeOf(uint8(0)), 2},
517+
err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeOf(uint8(0)), Offset: 2},
513518
},
514519
{
515520
in: `{"128":"abc"}`,
516521
ptr: new(map[int8]string),
517-
err: &UnmarshalTypeError{"number 128", reflect.TypeOf(int8(0)), 2},
522+
err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeOf(int8(0)), Offset: 2},
518523
},
519524
{
520525
in: `{"-1":"abc"}`,
521526
ptr: new(map[uint8]string),
522-
err: &UnmarshalTypeError{"number -1", reflect.TypeOf(uint8(0)), 2},
527+
err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeOf(uint8(0)), Offset: 2},
523528
},
524529

525530
// Map keys can be encoding.TextUnmarshalers.
@@ -653,12 +658,12 @@ var unmarshalTests = []unmarshalTest{
653658
{
654659
in: `{"2009-11-10T23:00:00Z": "hello world"}`,
655660
ptr: &map[Point]string{},
656-
err: &UnmarshalTypeError{"object", reflect.TypeOf(map[Point]string{}), 1},
661+
err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[Point]string{}), Offset: 1},
657662
},
658663
{
659664
in: `{"asdf": "hello world"}`,
660665
ptr: &map[unmarshaler]string{},
661-
err: &UnmarshalTypeError{"object", reflect.TypeOf(map[unmarshaler]string{}), 1},
666+
err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[unmarshaler]string{}), Offset: 1},
662667
},
663668

664669
// related to issue 13783.
@@ -750,6 +755,29 @@ var unmarshalTests = []unmarshalTest{
750755
{in: `999999999999999900000`, ptr: new(float64), out: 999999999999999900000.0, golden: true},
751756
{in: `9007199254740992`, ptr: new(float64), out: 9007199254740992.0, golden: true},
752757
{in: `9007199254740993`, ptr: new(float64), out: 9007199254740992.0, golden: false},
758+
759+
{
760+
in: `{"V": {"F2": "hello"}}`,
761+
ptr: new(VOuter),
762+
err: &UnmarshalTypeError{
763+
Value: "string",
764+
Struct: "V",
765+
Field: "F2",
766+
Type: reflect.TypeOf(int32(0)),
767+
Offset: 20,
768+
},
769+
},
770+
{
771+
in: `{"V": {"F4": {}, "F2": "hello"}}`,
772+
ptr: new(VOuter),
773+
err: &UnmarshalTypeError{
774+
Value: "string",
775+
Struct: "V",
776+
Field: "F2",
777+
Type: reflect.TypeOf(int32(0)),
778+
Offset: 30,
779+
},
780+
},
753781
}
754782

755783
func TestMarshal(t *testing.T) {

0 commit comments

Comments
 (0)