| // Copyright 2010 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package json | |
| import ( | |
| "bytes" | |
| "encoding" | |
| "fmt" | |
| "image" | |
| "net" | |
| "reflect" | |
| "strings" | |
| "testing" | |
| "time" | |
| ) | |
| type T struct { | |
| X string | |
| Y int | |
| Z int `json:"-"` | |
| } | |
| type U struct { | |
| Alphabet string `json:"alpha"` | |
| } | |
| type V struct { | |
| F1 interface{} | |
| F2 int32 | |
| F3 Number | |
| } | |
| // ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and | |
| // without UseNumber | |
| var ifaceNumAsFloat64 = map[string]interface{}{ | |
| "k1": float64(1), | |
| "k2": "s", | |
| "k3": []interface{}{float64(1), float64(2.0), float64(3e-3)}, | |
| "k4": map[string]interface{}{"kk1": "s", "kk2": float64(2)}, | |
| } | |
| var ifaceNumAsNumber = map[string]interface{}{ | |
| "k1": Number("1"), | |
| "k2": "s", | |
| "k3": []interface{}{Number("1"), Number("2.0"), Number("3e-3")}, | |
| "k4": map[string]interface{}{"kk1": "s", "kk2": Number("2")}, | |
| } | |
| type tx struct { | |
| x int | |
| } | |
| // A type that can unmarshal itself. | |
| type unmarshaler struct { | |
| T bool | |
| } | |
| func (u *unmarshaler) UnmarshalJSON(b []byte) error { | |
| *u = unmarshaler{true} // All we need to see that UnmarshalJSON is called. | |
| return nil | |
| } | |
| type ustruct struct { | |
| M unmarshaler | |
| } | |
| type unmarshalerText struct { | |
| T bool | |
| } | |
| // needed for re-marshaling tests | |
| func (u *unmarshalerText) MarshalText() ([]byte, error) { | |
| return []byte(""), nil | |
| } | |
| func (u *unmarshalerText) UnmarshalText(b []byte) error { | |
| *u = unmarshalerText{true} // All we need to see that UnmarshalText is called. | |
| return nil | |
| } | |
| var _ encoding.TextUnmarshaler = (*unmarshalerText)(nil) | |
| type ustructText struct { | |
| M unmarshalerText | |
| } | |
| var ( | |
| um0, um1 unmarshaler // target2 of unmarshaling | |
| ump = &um1 | |
| umtrue = unmarshaler{true} | |
| umslice = []unmarshaler{{true}} | |
| umslicep = new([]unmarshaler) | |
| umstruct = ustruct{unmarshaler{true}} | |
| um0T, um1T unmarshalerText // target2 of unmarshaling | |
| umpT = &um1T | |
| umtrueT = unmarshalerText{true} | |
| umsliceT = []unmarshalerText{{true}} | |
| umslicepT = new([]unmarshalerText) | |
| umstructT = ustructText{unmarshalerText{true}} | |
| ) | |
| // Test data structures for anonymous fields. | |
| type Point struct { | |
| Z int | |
| } | |
| type Top struct { | |
| Level0 int | |
| Embed0 | |
| *Embed0a | |
| *Embed0b `json:"e,omitempty"` // treated as named | |
| Embed0c `json:"-"` // ignored | |
| Loop | |
| Embed0p // has Point with X, Y, used | |
| Embed0q // has Point with Z, used | |
| embed // contains exported field | |
| } | |
| type Embed0 struct { | |
| Level1a int // overridden by Embed0a's Level1a with json tag | |
| Level1b int // used because Embed0a's Level1b is renamed | |
| Level1c int // used because Embed0a's Level1c is ignored | |
| Level1d int // annihilated by Embed0a's Level1d | |
| Level1e int `json:"x"` // annihilated by Embed0a.Level1e | |
| } | |
| type Embed0a struct { | |
| Level1a int `json:"Level1a,omitempty"` | |
| Level1b int `json:"LEVEL1B,omitempty"` | |
| Level1c int `json:"-"` | |
| Level1d int // annihilated by Embed0's Level1d | |
| Level1f int `json:"x"` // annihilated by Embed0's Level1e | |
| } | |
| type Embed0b Embed0 | |
| type Embed0c Embed0 | |
| type Embed0p struct { | |
| image.Point | |
| } | |
| type Embed0q struct { | |
| Point | |
| } | |
| type embed struct { | |
| Q int | |
| } | |
| type Loop struct { | |
| Loop1 int `json:",omitempty"` | |
| Loop2 int `json:",omitempty"` | |
| *Loop | |
| } | |
| // From reflect test: | |
| // The X in S6 and S7 annihilate, but they also block the X in S8.S9. | |
| type S5 struct { | |
| S6 | |
| S7 | |
| S8 | |
| } | |
| type S6 struct { | |
| X int | |
| } | |
| type S7 S6 | |
| type S8 struct { | |
| S9 | |
| } | |
| type S9 struct { | |
| X int | |
| Y int | |
| } | |
| // From reflect test: | |
| // The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. | |
| type S10 struct { | |
| S11 | |
| S12 | |
| S13 | |
| } | |
| type S11 struct { | |
| S6 | |
| } | |
| type S12 struct { | |
| S6 | |
| } | |
| type S13 struct { | |
| S8 | |
| } | |
| type unmarshalTest struct { | |
| in string | |
| ptr interface{} | |
| out interface{} | |
| err error | |
| useNumber bool | |
| } | |
| type Ambig struct { | |
| // Given "hello", the first match should win. | |
| First int `json:"HELLO"` | |
| Second int `json:"Hello"` | |
| } | |
| type XYZ struct { | |
| X interface{} | |
| Y interface{} | |
| Z interface{} | |
| } | |
| func sliceAddr(x []int) *[]int { return &x } | |
| func mapAddr(x map[string]int) *map[string]int { return &x } | |
| var unmarshalTests = []unmarshalTest{ | |
| // basic types | |
| {in: `true`, ptr: new(bool), out: true}, | |
| {in: `1`, ptr: new(int), out: 1}, | |
| {in: `1.2`, ptr: new(float64), out: 1.2}, | |
| {in: `-5`, ptr: new(int16), out: int16(-5)}, | |
| {in: `2`, ptr: new(Number), out: Number("2"), useNumber: true}, | |
| {in: `2`, ptr: new(Number), out: Number("2")}, | |
| {in: `2`, ptr: new(interface{}), out: float64(2.0)}, | |
| {in: `2`, ptr: new(interface{}), out: Number("2"), useNumber: true}, | |
| {in: `"a\u1234"`, ptr: new(string), out: "a\u1234"}, | |
| {in: `"http:\/\/"`, ptr: new(string), out: "http://"}, | |
| {in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"}, | |
| {in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"}, | |
| {in: "null", ptr: new(interface{}), out: nil}, | |
| {in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7}}, | |
| {in: `{"x": 1}`, ptr: new(tx), out: tx{}}, | |
| {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}}, | |
| {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true}, | |
| {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64}, | |
| {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsNumber, useNumber: true}, | |
| // raw values with whitespace | |
| {in: "\n true ", ptr: new(bool), out: true}, | |
| {in: "\t 1 ", ptr: new(int), out: 1}, | |
| {in: "\r 1.2 ", ptr: new(float64), out: 1.2}, | |
| {in: "\t -5 \n", ptr: new(int16), out: int16(-5)}, | |
| {in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"}, | |
| // Z has a "-" tag. | |
| {in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}}, | |
| {in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}}, | |
| {in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}}, | |
| {in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}}, | |
| // syntax errors | |
| {in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}}, | |
| {in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}}, | |
| {in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true}, | |
| // raw value errors | |
| {in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, | |
| {in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}}, | |
| {in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, | |
| {in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}}, | |
| {in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, | |
| {in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}}, | |
| {in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, | |
| {in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}}, | |
| // array tests | |
| {in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}}, | |
| {in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}}, | |
| {in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}}, | |
| // empty array to interface test | |
| {in: `[]`, ptr: new([]interface{}), out: []interface{}{}}, | |
| {in: `null`, ptr: new([]interface{}), out: []interface{}(nil)}, | |
| {in: `{"T":[]}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": []interface{}{}}}, | |
| {in: `{"T":null}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": interface{}(nil)}}, | |
| // composite tests | |
| {in: allValueIndent, ptr: new(All), out: allValue}, | |
| {in: allValueCompact, ptr: new(All), out: allValue}, | |
| {in: allValueIndent, ptr: new(*All), out: &allValue}, | |
| {in: allValueCompact, ptr: new(*All), out: &allValue}, | |
| {in: pallValueIndent, ptr: new(All), out: pallValue}, | |
| {in: pallValueCompact, ptr: new(All), out: pallValue}, | |
| {in: pallValueIndent, ptr: new(*All), out: &pallValue}, | |
| {in: pallValueCompact, ptr: new(*All), out: &pallValue}, | |
| // unmarshal interface test | |
| {in: `{"T":false}`, ptr: &um0, out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called | |
| {in: `{"T":false}`, ptr: &ump, out: &umtrue}, | |
| {in: `[{"T":false}]`, ptr: &umslice, out: umslice}, | |
| {in: `[{"T":false}]`, ptr: &umslicep, out: &umslice}, | |
| {in: `{"M":{"T":false}}`, ptr: &umstruct, out: umstruct}, | |
| // UnmarshalText interface test | |
| {in: `"X"`, ptr: &um0T, out: umtrueT}, // use "false" so test will fail if custom unmarshaler is not called | |
| {in: `"X"`, ptr: &umpT, out: &umtrueT}, | |
| {in: `["X"]`, ptr: &umsliceT, out: umsliceT}, | |
| {in: `["X"]`, ptr: &umslicepT, out: &umsliceT}, | |
| {in: `{"M":"X"}`, ptr: &umstructT, out: umstructT}, | |
| // Overwriting of data. | |
| // This is different from package xml, but it's what we've always done. | |
| // Now documented and tested. | |
| {in: `[2]`, ptr: sliceAddr([]int{1}), out: []int{2}}, | |
| {in: `{"key": 2}`, ptr: mapAddr(map[string]int{"old": 0, "key": 1}), out: map[string]int{"key": 2}}, | |
| { | |
| in: `{ | |
| "Level0": 1, | |
| "Level1b": 2, | |
| "Level1c": 3, | |
| "x": 4, | |
| "Level1a": 5, | |
| "LEVEL1B": 6, | |
| "e": { | |
| "Level1a": 8, | |
| "Level1b": 9, | |
| "Level1c": 10, | |
| "Level1d": 11, | |
| "x": 12 | |
| }, | |
| "Loop1": 13, | |
| "Loop2": 14, | |
| "X": 15, | |
| "Y": 16, | |
| "Z": 17, | |
| "Q": 18 | |
| }`, | |
| ptr: new(Top), | |
| out: Top{ | |
| Level0: 1, | |
| Embed0: Embed0{ | |
| Level1b: 2, | |
| Level1c: 3, | |
| }, | |
| Embed0a: &Embed0a{ | |
| Level1a: 5, | |
| Level1b: 6, | |
| }, | |
| Embed0b: &Embed0b{ | |
| Level1a: 8, | |
| Level1b: 9, | |
| Level1c: 10, | |
| Level1d: 11, | |
| Level1e: 12, | |
| }, | |
| Loop: Loop{ | |
| Loop1: 13, | |
| Loop2: 14, | |
| }, | |
| Embed0p: Embed0p{ | |
| Point: image.Point{X: 15, Y: 16}, | |
| }, | |
| Embed0q: Embed0q{ | |
| Point: Point{Z: 17}, | |
| }, | |
| embed: embed{ | |
| Q: 18, | |
| }, | |
| }, | |
| }, | |
| { | |
| in: `{"hello": 1}`, | |
| ptr: new(Ambig), | |
| out: Ambig{First: 1}, | |
| }, | |
| { | |
| in: `{"X": 1,"Y":2}`, | |
| ptr: new(S5), | |
| out: S5{S8: S8{S9: S9{Y: 2}}}, | |
| }, | |
| { | |
| in: `{"X": 1,"Y":2}`, | |
| ptr: new(S10), | |
| out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}}, | |
| }, | |
| // invalid UTF-8 is coerced to valid UTF-8. | |
| { | |
| in: "\"hello\xffworld\"", | |
| ptr: new(string), | |
| out: "hello\ufffdworld", | |
| }, | |
| { | |
| in: "\"hello\xc2\xc2world\"", | |
| ptr: new(string), | |
| out: "hello\ufffd\ufffdworld", | |
| }, | |
| { | |
| in: "\"hello\xc2\xffworld\"", | |
| ptr: new(string), | |
| out: "hello\ufffd\ufffdworld", | |
| }, | |
| { | |
| in: "\"hello\\ud800world\"", | |
| ptr: new(string), | |
| out: "hello\ufffdworld", | |
| }, | |
| { | |
| in: "\"hello\\ud800\\ud800world\"", | |
| ptr: new(string), | |
| out: "hello\ufffd\ufffdworld", | |
| }, | |
| { | |
| in: "\"hello\\ud800\\ud800world\"", | |
| ptr: new(string), | |
| out: "hello\ufffd\ufffdworld", | |
| }, | |
| { | |
| in: "\"hello\xed\xa0\x80\xed\xb0\x80world\"", | |
| ptr: new(string), | |
| out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld", | |
| }, | |
| // issue 8305 | |
| { | |
| in: `{"2009-11-10T23:00:00Z": "hello world"}`, | |
| ptr: &map[time.Time]string{}, | |
| err: &UnmarshalTypeError{"object", reflect.TypeOf(map[time.Time]string{}), 1}, | |
| }, | |
| } | |
| func TestMarshal(t *testing.T) { | |
| b, err := Marshal(allValue) | |
| if err != nil { | |
| t.Fatalf("Marshal allValue: %v", err) | |
| } | |
| if string(b) != allValueCompact { | |
| t.Errorf("Marshal allValueCompact") | |
| diff(t, b, []byte(allValueCompact)) | |
| return | |
| } | |
| b, err = Marshal(pallValue) | |
| if err != nil { | |
| t.Fatalf("Marshal pallValue: %v", err) | |
| } | |
| if string(b) != pallValueCompact { | |
| t.Errorf("Marshal pallValueCompact") | |
| diff(t, b, []byte(pallValueCompact)) | |
| return | |
| } | |
| } | |
| var badUTF8 = []struct { | |
| in, out string | |
| }{ | |
| {"hello\xffworld", `"hello\ufffdworld"`}, | |
| {"", `""`}, | |
| {"\xff", `"\ufffd"`}, | |
| {"\xff\xff", `"\ufffd\ufffd"`}, | |
| {"a\xffb", `"a\ufffdb"`}, | |
| {"\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"日本\ufffd\ufffd\ufffd"`}, | |
| } | |
| func TestMarshalBadUTF8(t *testing.T) { | |
| for _, tt := range badUTF8 { | |
| b, err := Marshal(tt.in) | |
| if string(b) != tt.out || err != nil { | |
| t.Errorf("Marshal(%q) = %#q, %v, want %#q, nil", tt.in, b, err, tt.out) | |
| } | |
| } | |
| } | |
| func TestMarshalNumberZeroVal(t *testing.T) { | |
| var n Number | |
| out, err := Marshal(n) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| outStr := string(out) | |
| if outStr != "0" { | |
| t.Fatalf("Invalid zero val for Number: %q", outStr) | |
| } | |
| } | |
| func TestMarshalEmbeds(t *testing.T) { | |
| top := &Top{ | |
| Level0: 1, | |
| Embed0: Embed0{ | |
| Level1b: 2, | |
| Level1c: 3, | |
| }, | |
| Embed0a: &Embed0a{ | |
| Level1a: 5, | |
| Level1b: 6, | |
| }, | |
| Embed0b: &Embed0b{ | |
| Level1a: 8, | |
| Level1b: 9, | |
| Level1c: 10, | |
| Level1d: 11, | |
| Level1e: 12, | |
| }, | |
| Loop: Loop{ | |
| Loop1: 13, | |
| Loop2: 14, | |
| }, | |
| Embed0p: Embed0p{ | |
| Point: image.Point{X: 15, Y: 16}, | |
| }, | |
| Embed0q: Embed0q{ | |
| Point: Point{Z: 17}, | |
| }, | |
| embed: embed{ | |
| Q: 18, | |
| }, | |
| } | |
| b, err := Marshal(top) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17,\"Q\":18}" | |
| if string(b) != want { | |
| t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want) | |
| } | |
| } | |
| func TestUnmarshal(t *testing.T) { | |
| for i, tt := range unmarshalTests { | |
| var scan scanner | |
| in := []byte(tt.in) | |
| if err := checkValid(in, &scan); err != nil { | |
| if !reflect.DeepEqual(err, tt.err) { | |
| t.Errorf("#%d: checkValid: %#v", i, err) | |
| continue | |
| } | |
| } | |
| if tt.ptr == nil { | |
| continue | |
| } | |
| // v = new(right-type) | |
| v := reflect.New(reflect.TypeOf(tt.ptr).Elem()) | |
| dec := NewDecoder(bytes.NewReader(in)) | |
| if tt.useNumber { | |
| dec.UseNumber() | |
| } | |
| if err := dec.Decode(v.Interface()); !reflect.DeepEqual(err, tt.err) { | |
| t.Errorf("#%d: %v, want %v", i, err, tt.err) | |
| continue | |
| } else if err != nil { | |
| continue | |
| } | |
| if !reflect.DeepEqual(v.Elem().Interface(), tt.out) { | |
| t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out) | |
| data, _ := Marshal(v.Elem().Interface()) | |
| println(string(data)) | |
| data, _ = Marshal(tt.out) | |
| println(string(data)) | |
| continue | |
| } | |
| // Check round trip. | |
| if tt.err == nil { | |
| enc, err := Marshal(v.Interface()) | |
| if err != nil { | |
| t.Errorf("#%d: error re-marshaling: %v", i, err) | |
| continue | |
| } | |
| vv := reflect.New(reflect.TypeOf(tt.ptr).Elem()) | |
| dec = NewDecoder(bytes.NewReader(enc)) | |
| if tt.useNumber { | |
| dec.UseNumber() | |
| } | |
| if err := dec.Decode(vv.Interface()); err != nil { | |
| t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err) | |
| continue | |
| } | |
| if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) { | |
| t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface()) | |
| t.Errorf(" In: %q", strings.Map(noSpace, string(in))) | |
| t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc))) | |
| continue | |
| } | |
| } | |
| } | |
| } | |
| func TestUnmarshalMarshal(t *testing.T) { | |
| initBig() | |
| var v interface{} | |
| if err := Unmarshal(jsonBig, &v); err != nil { | |
| t.Fatalf("Unmarshal: %v", err) | |
| } | |
| b, err := Marshal(v) | |
| if err != nil { | |
| t.Fatalf("Marshal: %v", err) | |
| } | |
| if !bytes.Equal(jsonBig, b) { | |
| t.Errorf("Marshal jsonBig") | |
| diff(t, b, jsonBig) | |
| return | |
| } | |
| } | |
| var numberTests = []struct { | |
| in string | |
| i int64 | |
| intErr string | |
| f float64 | |
| floatErr string | |
| }{ | |
| {in: "-1.23e1", intErr: "strconv.ParseInt: parsing \"-1.23e1\": invalid syntax", f: -1.23e1}, | |
| {in: "-12", i: -12, f: -12.0}, | |
| {in: "1e1000", intErr: "strconv.ParseInt: parsing \"1e1000\": invalid syntax", floatErr: "strconv.ParseFloat: parsing \"1e1000\": value out of range"}, | |
| } | |
| // Independent of Decode, basic coverage of the accessors in Number | |
| func TestNumberAccessors(t *testing.T) { | |
| for _, tt := range numberTests { | |
| n := Number(tt.in) | |
| if s := n.String(); s != tt.in { | |
| t.Errorf("Number(%q).String() is %q", tt.in, s) | |
| } | |
| if i, err := n.Int64(); err == nil && tt.intErr == "" && i != tt.i { | |
| t.Errorf("Number(%q).Int64() is %d", tt.in, i) | |
| } else if (err == nil && tt.intErr != "") || (err != nil && err.Error() != tt.intErr) { | |
| t.Errorf("Number(%q).Int64() wanted error %q but got: %v", tt.in, tt.intErr, err) | |
| } | |
| if f, err := n.Float64(); err == nil && tt.floatErr == "" && f != tt.f { | |
| t.Errorf("Number(%q).Float64() is %g", tt.in, f) | |
| } else if (err == nil && tt.floatErr != "") || (err != nil && err.Error() != tt.floatErr) { | |
| t.Errorf("Number(%q).Float64() wanted error %q but got: %v", tt.in, tt.floatErr, err) | |
| } | |
| } | |
| } | |
| func TestLargeByteSlice(t *testing.T) { | |
| s0 := make([]byte, 2000) | |
| for i := range s0 { | |
| s0[i] = byte(i) | |
| } | |
| b, err := Marshal(s0) | |
| if err != nil { | |
| t.Fatalf("Marshal: %v", err) | |
| } | |
| var s1 []byte | |
| if err := Unmarshal(b, &s1); err != nil { | |
| t.Fatalf("Unmarshal: %v", err) | |
| } | |
| if !bytes.Equal(s0, s1) { | |
| t.Errorf("Marshal large byte slice") | |
| diff(t, s0, s1) | |
| } | |
| } | |
| type Xint struct { | |
| X int | |
| } | |
| func TestUnmarshalInterface(t *testing.T) { | |
| var xint Xint | |
| var i interface{} = &xint | |
| if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil { | |
| t.Fatalf("Unmarshal: %v", err) | |
| } | |
| if xint.X != 1 { | |
| t.Fatalf("Did not write to xint") | |
| } | |
| } | |
| func TestUnmarshalPtrPtr(t *testing.T) { | |
| var xint Xint | |
| pxint := &xint | |
| if err := Unmarshal([]byte(`{"X":1}`), &pxint); err != nil { | |
| t.Fatalf("Unmarshal: %v", err) | |
| } | |
| if xint.X != 1 { | |
| t.Fatalf("Did not write to xint") | |
| } | |
| } | |
| func TestEscape(t *testing.T) { | |
| const input = `"foobar"<html>` + " [\u2028 \u2029]" | |
| const expected = `"\"foobar\"\u003chtml\u003e [\u2028 \u2029]"` | |
| b, err := Marshal(input) | |
| if err != nil { | |
| t.Fatalf("Marshal error: %v", err) | |
| } | |
| if s := string(b); s != expected { | |
| t.Errorf("Encoding of [%s]:\n got [%s]\nwant [%s]", input, s, expected) | |
| } | |
| } | |
| // WrongString is a struct that's misusing the ,string modifier. | |
| type WrongString struct { | |
| Message string `json:"result,string"` | |
| } | |
| type wrongStringTest struct { | |
| in, err string | |
| } | |
| var wrongStringTests = []wrongStringTest{ | |
| {`{"result":"x"}`, `json: invalid use of ,string struct tag, trying to unmarshal "x" into string`}, | |
| {`{"result":"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "foo" into string`}, | |
| {`{"result":"123"}`, `json: invalid use of ,string struct tag, trying to unmarshal "123" into string`}, | |
| {`{"result":123}`, `json: invalid use of ,string struct tag, trying to unmarshal unquoted value into string`}, | |
| } | |
| // If people misuse the ,string modifier, the error message should be | |
| // helpful, telling the user that they're doing it wrong. | |
| func TestErrorMessageFromMisusedString(t *testing.T) { | |
| for n, tt := range wrongStringTests { | |
| r := strings.NewReader(tt.in) | |
| var s WrongString | |
| err := NewDecoder(r).Decode(&s) | |
| got := fmt.Sprintf("%v", err) | |
| if got != tt.err { | |
| t.Errorf("%d. got err = %q, want %q", n, got, tt.err) | |
| } | |
| } | |
| } | |
| func noSpace(c rune) rune { | |
| if isSpace(byte(c)) { //only used for ascii | |
| return -1 | |
| } | |
| return c | |
| } | |
| type All struct { | |
| Bool bool | |
| Int int | |
| Int8 int8 | |
| Int16 int16 | |
| Int32 int32 | |
| Int64 int64 | |
| Uint uint | |
| Uint8 uint8 | |
| Uint16 uint16 | |
| Uint32 uint32 | |
| Uint64 uint64 | |
| Uintptr uintptr | |
| Float32 float32 | |
| Float64 float64 | |
| Foo string `json:"bar"` | |
| Foo2 string `json:"bar2,dummyopt"` | |
| IntStr int64 `json:",string"` | |
| PBool *bool | |
| PInt *int | |
| PInt8 *int8 | |
| PInt16 *int16 | |
| PInt32 *int32 | |
| PInt64 *int64 | |
| PUint *uint | |
| PUint8 *uint8 | |
| PUint16 *uint16 | |
| PUint32 *uint32 | |
| PUint64 *uint64 | |
| PUintptr *uintptr | |
| PFloat32 *float32 | |
| PFloat64 *float64 | |
| String string | |
| PString *string | |
| Map map[string]Small | |
| MapP map[string]*Small | |
| PMap *map[string]Small | |
| PMapP *map[string]*Small | |
| EmptyMap map[string]Small | |
| NilMap map[string]Small | |
| Slice []Small | |
| SliceP []*Small | |
| PSlice *[]Small | |
| PSliceP *[]*Small | |
| EmptySlice []Small | |
| NilSlice []Small | |
| StringSlice []string | |
| ByteSlice []byte | |
| Small Small | |
| PSmall *Small | |
| PPSmall **Small | |
| Interface interface{} | |
| PInterface *interface{} | |
| unexported int | |
| } | |
| type Small struct { | |
| Tag string | |
| } | |
| var allValue = All{ | |
| Bool: true, | |
| Int: 2, | |
| Int8: 3, | |
| Int16: 4, | |
| Int32: 5, | |
| Int64: 6, | |
| Uint: 7, | |
| Uint8: 8, | |
| Uint16: 9, | |
| Uint32: 10, | |
| Uint64: 11, | |
| Uintptr: 12, | |
| Float32: 14.1, | |
| Float64: 15.1, | |
| Foo: "foo", | |
| Foo2: "foo2", | |
| IntStr: 42, | |
| String: "16", | |
| Map: map[string]Small{ | |
| "17": {Tag: "tag17"}, | |
| "18": {Tag: "tag18"}, | |
| }, | |
| MapP: map[string]*Small{ | |
| "19": {Tag: "tag19"}, | |
| "20": nil, | |
| }, | |
| EmptyMap: map[string]Small{}, | |
| Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}}, | |
| SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}}, | |
| EmptySlice: []Small{}, | |
| StringSlice: []string{"str24", "str25", "str26"}, | |
| ByteSlice: []byte{27, 28, 29}, | |
| Small: Small{Tag: "tag30"}, | |
| PSmall: &Small{Tag: "tag31"}, | |
| Interface: 5.2, | |
| } | |
| var pallValue = All{ | |
| PBool: &allValue.Bool, | |
| PInt: &allValue.Int, | |
| PInt8: &allValue.Int8, | |
| PInt16: &allValue.Int16, | |
| PInt32: &allValue.Int32, | |
| PInt64: &allValue.Int64, | |
| PUint: &allValue.Uint, | |
| PUint8: &allValue.Uint8, | |
| PUint16: &allValue.Uint16, | |
| PUint32: &allValue.Uint32, | |
| PUint64: &allValue.Uint64, | |
| PUintptr: &allValue.Uintptr, | |
| PFloat32: &allValue.Float32, | |
| PFloat64: &allValue.Float64, | |
| PString: &allValue.String, | |
| PMap: &allValue.Map, | |
| PMapP: &allValue.MapP, | |
| PSlice: &allValue.Slice, | |
| PSliceP: &allValue.SliceP, | |
| PPSmall: &allValue.PSmall, | |
| PInterface: &allValue.Interface, | |
| } | |
| var allValueIndent = `{ | |
| "Bool": true, | |
| "Int": 2, | |
| "Int8": 3, | |
| "Int16": 4, | |
| "Int32": 5, | |
| "Int64": 6, | |
| "Uint": 7, | |
| "Uint8": 8, | |
| "Uint16": 9, | |
| "Uint32": 10, | |
| "Uint64": 11, | |
| "Uintptr": 12, | |
| "Float32": 14.1, | |
| "Float64": 15.1, | |
| "bar": "foo", | |
| "bar2": "foo2", | |
| "IntStr": "42", | |
| "PBool": null, | |
| "PInt": null, | |
| "PInt8": null, | |
| "PInt16": null, | |
| "PInt32": null, | |
| "PInt64": null, | |
| "PUint": null, | |
| "PUint8": null, | |
| "PUint16": null, | |
| "PUint32": null, | |
| "PUint64": null, | |
| "PUintptr": null, | |
| "PFloat32": null, | |
| "PFloat64": null, | |
| "String": "16", | |
| "PString": null, | |
| "Map": { | |
| "17": { | |
| "Tag": "tag17" | |
| }, | |
| "18": { | |
| "Tag": "tag18" | |
| } | |
| }, | |
| "MapP": { | |
| "19": { | |
| "Tag": "tag19" | |
| }, | |
| "20": null | |
| }, | |
| "PMap": null, | |
| "PMapP": null, | |
| "EmptyMap": {}, | |
| "NilMap": null, | |
| "Slice": [ | |
| { | |
| "Tag": "tag20" | |
| }, | |
| { | |
| "Tag": "tag21" | |
| } | |
| ], | |
| "SliceP": [ | |
| { | |
| "Tag": "tag22" | |
| }, | |
| null, | |
| { | |
| "Tag": "tag23" | |
| } | |
| ], | |
| "PSlice": null, | |
| "PSliceP": null, | |
| "EmptySlice": [], | |
| "NilSlice": null, | |
| "StringSlice": [ | |
| "str24", | |
| "str25", | |
| "str26" | |
| ], | |
| "ByteSlice": "Gxwd", | |
| "Small": { | |
| "Tag": "tag30" | |
| }, | |
| "PSmall": { | |
| "Tag": "tag31" | |
| }, | |
| "PPSmall": null, | |
| "Interface": 5.2, | |
| "PInterface": null | |
| }` | |
| var allValueCompact = strings.Map(noSpace, allValueIndent) | |
| var pallValueIndent = `{ | |
| "Bool": false, | |
| "Int": 0, | |
| "Int8": 0, | |
| "Int16": 0, | |
| "Int32": 0, | |
| "Int64": 0, | |
| "Uint": 0, | |
| "Uint8": 0, | |
| "Uint16": 0, | |
| "Uint32": 0, | |
| "Uint64": 0, | |
| "Uintptr": 0, | |
| "Float32": 0, | |
| "Float64": 0, | |
| "bar": "", | |
| "bar2": "", | |
| "IntStr": "0", | |
| "PBool": true, | |
| "PInt": 2, | |
| "PInt8": 3, | |
| "PInt16": 4, | |
| "PInt32": 5, | |
| "PInt64": 6, | |
| "PUint": 7, | |
| "PUint8": 8, | |
| "PUint16": 9, | |
| "PUint32": 10, | |
| "PUint64": 11, | |
| "PUintptr": 12, | |
| "PFloat32": 14.1, | |
| "PFloat64": 15.1, | |
| "String": "", | |
| "PString": "16", | |
| "Map": null, | |
| "MapP": null, | |
| "PMap": { | |
| "17": { | |
| "Tag": "tag17" | |
| }, | |
| "18": { | |
| "Tag": "tag18" | |
| } | |
| }, | |
| "PMapP": { | |
| "19": { | |
| "Tag": "tag19" | |
| }, | |
| "20": null | |
| }, | |
| "EmptyMap": null, | |
| "NilMap": null, | |
| "Slice": null, | |
| "SliceP": null, | |
| "PSlice": [ | |
| { | |
| "Tag": "tag20" | |
| }, | |
| { | |
| "Tag": "tag21" | |
| } | |
| ], | |
| "PSliceP": [ | |
| { | |
| "Tag": "tag22" | |
| }, | |
| null, | |
| { | |
| "Tag": "tag23" | |
| } | |
| ], | |
| "EmptySlice": null, | |
| "NilSlice": null, | |
| "StringSlice": null, | |
| "ByteSlice": null, | |
| "Small": { | |
| "Tag": "" | |
| }, | |
| "PSmall": null, | |
| "PPSmall": { | |
| "Tag": "tag31" | |
| }, | |
| "Interface": null, | |
| "PInterface": 5.2 | |
| }` | |
| var pallValueCompact = strings.Map(noSpace, pallValueIndent) | |
| func TestRefUnmarshal(t *testing.T) { | |
| type S struct { | |
| // Ref is defined in encode_test.go. | |
| R0 Ref | |
| R1 *Ref | |
| R2 RefText | |
| R3 *RefText | |
| } | |
| want := S{ | |
| R0: 12, | |
| R1: new(Ref), | |
| R2: 13, | |
| R3: new(RefText), | |
| } | |
| *want.R1 = 12 | |
| *want.R3 = 13 | |
| var got S | |
| if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref","R2":"ref","R3":"ref"}`), &got); err != nil { | |
| t.Fatalf("Unmarshal: %v", err) | |
| } | |
| if !reflect.DeepEqual(got, want) { | |
| t.Errorf("got %+v, want %+v", got, want) | |
| } | |
| } | |
| // Test that the empty string doesn't panic decoding when ,string is specified | |
| // Issue 3450 | |
| func TestEmptyString(t *testing.T) { | |
| type T2 struct { | |
| Number1 int `json:",string"` | |
| Number2 int `json:",string"` | |
| } | |
| data := `{"Number1":"1", "Number2":""}` | |
| dec := NewDecoder(strings.NewReader(data)) | |
| var t2 T2 | |
| err := dec.Decode(&t2) | |
| if err == nil { | |
| t.Fatal("Decode: did not return error") | |
| } | |
| if t2.Number1 != 1 { | |
| t.Fatal("Decode: did not set Number1") | |
| } | |
| } | |
| // Test that a null for ,string is not replaced with the previous quoted string (issue 7046). | |
| // It should also not be an error (issue 2540, issue 8587). | |
| func TestNullString(t *testing.T) { | |
| type T struct { | |
| A int `json:",string"` | |
| B int `json:",string"` | |
| C *int `json:",string"` | |
| } | |
| data := []byte(`{"A": "1", "B": null, "C": null}`) | |
| var s T | |
| s.B = 1 | |
| s.C = new(int) | |
| *s.C = 2 | |
| err := Unmarshal(data, &s) | |
| if err != nil { | |
| t.Fatalf("Unmarshal: %v", err) | |
| } | |
| if s.B != 1 || s.C != nil { | |
| t.Fatalf("after Unmarshal, s.B=%d, s.C=%p, want 1, nil", s.B, s.C) | |
| } | |
| } | |
| func intp(x int) *int { | |
| p := new(int) | |
| *p = x | |
| return p | |
| } | |
| func intpp(x *int) **int { | |
| pp := new(*int) | |
| *pp = x | |
| return pp | |
| } | |
| var interfaceSetTests = []struct { | |
| pre interface{} | |
| json string | |
| post interface{} | |
| }{ | |
| {"foo", `"bar"`, "bar"}, | |
| {"foo", `2`, 2.0}, | |
| {"foo", `true`, true}, | |
| {"foo", `null`, nil}, | |
| {nil, `null`, nil}, | |
| {new(int), `null`, nil}, | |
| {(*int)(nil), `null`, nil}, | |
| {new(*int), `null`, new(*int)}, | |
| {(**int)(nil), `null`, nil}, | |
| {intp(1), `null`, nil}, | |
| {intpp(nil), `null`, intpp(nil)}, | |
| {intpp(intp(1)), `null`, intpp(nil)}, | |
| } | |
| func TestInterfaceSet(t *testing.T) { | |
| for _, tt := range interfaceSetTests { | |
| b := struct{ X interface{} }{tt.pre} | |
| blob := `{"X":` + tt.json + `}` | |
| if err := Unmarshal([]byte(blob), &b); err != nil { | |
| t.Errorf("Unmarshal %#q: %v", blob, err) | |
| continue | |
| } | |
| if !reflect.DeepEqual(b.X, tt.post) { | |
| t.Errorf("Unmarshal %#q into %#v: X=%#v, want %#v", blob, tt.pre, b.X, tt.post) | |
| } | |
| } | |
| } | |
| // JSON null values should be ignored for primitives and string values instead of resulting in an error. | |
| // Issue 2540 | |
| func TestUnmarshalNulls(t *testing.T) { | |
| jsonData := []byte(`{ | |
| "Bool" : null, | |
| "Int" : null, | |
| "Int8" : null, | |
| "Int16" : null, | |
| "Int32" : null, | |
| "Int64" : null, | |
| "Uint" : null, | |
| "Uint8" : null, | |
| "Uint16" : null, | |
| "Uint32" : null, | |
| "Uint64" : null, | |
| "Float32" : null, | |
| "Float64" : null, | |
| "String" : null}`) | |
| nulls := All{ | |
| Bool: true, | |
| Int: 2, | |
| Int8: 3, | |
| Int16: 4, | |
| Int32: 5, | |
| Int64: 6, | |
| Uint: 7, | |
| Uint8: 8, | |
| Uint16: 9, | |
| Uint32: 10, | |
| Uint64: 11, | |
| Float32: 12.1, | |
| Float64: 13.1, | |
| String: "14"} | |
| err := Unmarshal(jsonData, &nulls) | |
| if err != nil { | |
| t.Errorf("Unmarshal of null values failed: %v", err) | |
| } | |
| if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 || | |
| nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 || | |
| nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" { | |
| t.Errorf("Unmarshal of null values affected primitives") | |
| } | |
| } | |
| func TestStringKind(t *testing.T) { | |
| type stringKind string | |
| var m1, m2 map[stringKind]int | |
| m1 = map[stringKind]int{ | |
| "foo": 42, | |
| } | |
| data, err := Marshal(m1) | |
| if err != nil { | |
| t.Errorf("Unexpected error marshaling: %v", err) | |
| } | |
| err = Unmarshal(data, &m2) | |
| if err != nil { | |
| t.Errorf("Unexpected error unmarshaling: %v", err) | |
| } | |
| if !reflect.DeepEqual(m1, m2) { | |
| t.Error("Items should be equal after encoding and then decoding") | |
| } | |
| } | |
| // Custom types with []byte as underlying type could not be marshalled | |
| // and then unmarshalled. | |
| // Issue 8962. | |
| func TestByteKind(t *testing.T) { | |
| type byteKind []byte | |
| a := byteKind("hello") | |
| data, err := Marshal(a) | |
| if err != nil { | |
| t.Error(err) | |
| } | |
| var b byteKind | |
| err = Unmarshal(data, &b) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if !reflect.DeepEqual(a, b) { | |
| t.Errorf("expected %v == %v", a, b) | |
| } | |
| } | |
| // The fix for issue 8962 introduced a regression. | |
| // Issue 12921. | |
| func TestSliceOfCustomByte(t *testing.T) { | |
| type Uint8 uint8 | |
| a := []Uint8("hello") | |
| data, err := Marshal(a) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| var b []Uint8 | |
| err = Unmarshal(data, &b) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if !reflect.DeepEqual(a, b) { | |
| t.Fatal("expected %v == %v", a, b) | |
| } | |
| } | |
| var decodeTypeErrorTests = []struct { | |
| dest interface{} | |
| src string | |
| }{ | |
| {new(string), `{"user": "name"}`}, // issue 4628. | |
| {new(error), `{}`}, // issue 4222 | |
| {new(error), `[]`}, | |
| {new(error), `""`}, | |
| {new(error), `123`}, | |
| {new(error), `true`}, | |
| } | |
| func TestUnmarshalTypeError(t *testing.T) { | |
| for _, item := range decodeTypeErrorTests { | |
| err := Unmarshal([]byte(item.src), item.dest) | |
| if _, ok := err.(*UnmarshalTypeError); !ok { | |
| t.Errorf("expected type error for Unmarshal(%q, type %T): got %T", | |
| item.src, item.dest, err) | |
| } | |
| } | |
| } | |
| var unmarshalSyntaxTests = []string{ | |
| "tru", | |
| "fals", | |
| "nul", | |
| "123e", | |
| `"hello`, | |
| `[1,2,3`, | |
| `{"key":1`, | |
| `{"key":1,`, | |
| } | |
| func TestUnmarshalSyntax(t *testing.T) { | |
| var x interface{} | |
| for _, src := range unmarshalSyntaxTests { | |
| err := Unmarshal([]byte(src), &x) | |
| if _, ok := err.(*SyntaxError); !ok { | |
| t.Errorf("expected syntax error for Unmarshal(%q): got %T", src, err) | |
| } | |
| } | |
| } | |
| // Test handling of unexported fields that should be ignored. | |
| // Issue 4660 | |
| type unexportedFields struct { | |
| Name string | |
| m map[string]interface{} `json:"-"` | |
| m2 map[string]interface{} `json:"abcd"` | |
| } | |
| func TestUnmarshalUnexported(t *testing.T) { | |
| input := `{"Name": "Bob", "m": {"x": 123}, "m2": {"y": 456}, "abcd": {"z": 789}}` | |
| want := &unexportedFields{Name: "Bob"} | |
| out := &unexportedFields{} | |
| err := Unmarshal([]byte(input), out) | |
| if err != nil { | |
| t.Errorf("got error %v, expected nil", err) | |
| } | |
| if !reflect.DeepEqual(out, want) { | |
| t.Errorf("got %q, want %q", out, want) | |
| } | |
| } | |
| // Time3339 is a time.Time which encodes to and from JSON | |
| // as an RFC 3339 time in UTC. | |
| type Time3339 time.Time | |
| func (t *Time3339) UnmarshalJSON(b []byte) error { | |
| if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { | |
| return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b) | |
| } | |
| tm, err := time.Parse(time.RFC3339, string(b[1:len(b)-1])) | |
| if err != nil { | |
| return err | |
| } | |
| *t = Time3339(tm) | |
| return nil | |
| } | |
| func TestUnmarshalJSONLiteralError(t *testing.T) { | |
| var t3 Time3339 | |
| err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3) | |
| if err == nil { | |
| t.Fatalf("expected error; got time %v", time.Time(t3)) | |
| } | |
| if !strings.Contains(err.Error(), "range") { | |
| t.Errorf("got err = %v; want out of range error", err) | |
| } | |
| } | |
| // Test that extra object elements in an array do not result in a | |
| // "data changing underfoot" error. | |
| // Issue 3717 | |
| func TestSkipArrayObjects(t *testing.T) { | |
| json := `[{}]` | |
| var dest [0]interface{} | |
| err := Unmarshal([]byte(json), &dest) | |
| if err != nil { | |
| t.Errorf("got error %q, want nil", err) | |
| } | |
| } | |
| // Test semantics of pre-filled struct fields and pre-filled map fields. | |
| // Issue 4900. | |
| func TestPrefilled(t *testing.T) { | |
| ptrToMap := func(m map[string]interface{}) *map[string]interface{} { return &m } | |
| // Values here change, cannot reuse table across runs. | |
| var prefillTests = []struct { | |
| in string | |
| ptr interface{} | |
| out interface{} | |
| }{ | |
| { | |
| in: `{"X": 1, "Y": 2}`, | |
| ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5}, | |
| out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5}, | |
| }, | |
| { | |
| in: `{"X": 1, "Y": 2}`, | |
| ptr: ptrToMap(map[string]interface{}{"X": float32(3), "Y": int16(4), "Z": 1.5}), | |
| out: ptrToMap(map[string]interface{}{"X": float64(1), "Y": float64(2), "Z": 1.5}), | |
| }, | |
| } | |
| for _, tt := range prefillTests { | |
| ptrstr := fmt.Sprintf("%v", tt.ptr) | |
| err := Unmarshal([]byte(tt.in), tt.ptr) // tt.ptr edited here | |
| if err != nil { | |
| t.Errorf("Unmarshal: %v", err) | |
| } | |
| if !reflect.DeepEqual(tt.ptr, tt.out) { | |
| t.Errorf("Unmarshal(%#q, %s): have %v, want %v", tt.in, ptrstr, tt.ptr, tt.out) | |
| } | |
| } | |
| } | |
| var invalidUnmarshalTests = []struct { | |
| v interface{} | |
| want string | |
| }{ | |
| {nil, "json: Unmarshal(nil)"}, | |
| {struct{}{}, "json: Unmarshal(non-pointer struct {})"}, | |
| {(*int)(nil), "json: Unmarshal(nil *int)"}, | |
| } | |
| func TestInvalidUnmarshal(t *testing.T) { | |
| buf := []byte(`{"a":"1"}`) | |
| for _, tt := range invalidUnmarshalTests { | |
| err := Unmarshal(buf, tt.v) | |
| if err == nil { | |
| t.Errorf("Unmarshal expecting error, got nil") | |
| continue | |
| } | |
| if got := err.Error(); got != tt.want { | |
| t.Errorf("Unmarshal = %q; want %q", got, tt.want) | |
| } | |
| } | |
| } | |
| var invalidUnmarshalTextTests = []struct { | |
| v interface{} | |
| want string | |
| }{ | |
| {nil, "json: Unmarshal(nil)"}, | |
| {struct{}{}, "json: Unmarshal(non-pointer struct {})"}, | |
| {(*int)(nil), "json: Unmarshal(nil *int)"}, | |
| {new(net.IP), "json: cannot unmarshal string into Go value of type *net.IP"}, | |
| } | |
| func TestInvalidUnmarshalText(t *testing.T) { | |
| buf := []byte(`123`) | |
| for _, tt := range invalidUnmarshalTextTests { | |
| err := Unmarshal(buf, tt.v) | |
| if err == nil { | |
| t.Errorf("Unmarshal expecting error, got nil") | |
| continue | |
| } | |
| if got := err.Error(); got != tt.want { | |
| t.Errorf("Unmarshal = %q; want %q", got, tt.want) | |
| } | |
| } | |
| } | |
| // Test that string option is ignored for invalid types. | |
| // Issue 9812. | |
| func TestInvalidStringOption(t *testing.T) { | |
| num := 0 | |
| item := struct { | |
| T time.Time `json:",string"` | |
| M map[string]string `json:",string"` | |
| S []string `json:",string"` | |
| A [1]string `json:",string"` | |
| I interface{} `json:",string"` | |
| P *int `json:",string"` | |
| }{M: make(map[string]string), S: make([]string, 0), I: num, P: &num} | |
| data, err := Marshal(item) | |
| if err != nil { | |
| t.Fatalf("Marshal: %v", err) | |
| } | |
| err = Unmarshal(data, &item) | |
| if err != nil { | |
| t.Fatalf("Unmarshal: %v", err) | |
| } | |
| } |