| // 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" | |
| "fmt" | |
| "reflect" | |
| "strings" | |
| "testing" | |
| ) | |
| type T struct { | |
| X string | |
| Y int | |
| Z int `json:"-"` | |
| } | |
| type U struct { | |
| Alphabet string `json:"alpha"` | |
| } | |
| type tx struct { | |
| x int | |
| } | |
| var txType = reflect.TypeOf((*tx)(nil)).Elem() | |
| // 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 | |
| } | |
| var ( | |
| um0, um1 unmarshaler // target2 of unmarshaling | |
| ump = &um1 | |
| umtrue = unmarshaler{true} | |
| umslice = []unmarshaler{{true}} | |
| umslicep = new([]unmarshaler) | |
| umstruct = ustruct{unmarshaler{true}} | |
| ) | |
| type unmarshalTest struct { | |
| in string | |
| ptr interface{} | |
| out interface{} | |
| err error | |
| } | |
| 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: `"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("")}}, | |
| {in: `{"x": 1}`, ptr: new(tx), out: tx{}, err: &UnmarshalFieldError{"x", txType, txType.Field(0)}}, | |
| // 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}}, | |
| // 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}}, | |
| // 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}, | |
| } | |
| 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 | |
| } | |
| } | |
| func TestMarshalBadUTF8(t *testing.T) { | |
| s := "hello\xffworld" | |
| b, err := Marshal(s) | |
| if err == nil { | |
| t.Fatal("Marshal bad UTF8: no error") | |
| } | |
| if len(b) != 0 { | |
| t.Fatal("Marshal returned data") | |
| } | |
| if _, ok := err.(*InvalidUTF8Error); !ok { | |
| t.Fatalf("Marshal did not return InvalidUTF8Error: %T %v", err, err) | |
| } | |
| } | |
| 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()) | |
| if err := Unmarshal([]byte(in), v.Interface()); !reflect.DeepEqual(err, tt.err) { | |
| t.Errorf("#%d: %v want %v", i, err, tt.err) | |
| 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()) | |
| if err := Unmarshal(enc, vv.Interface()); err != nil { | |
| t.Errorf("#%d: error re-unmarshaling: %v", i, 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()) | |
| 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.Compare(jsonBig, b) != 0 { | |
| t.Errorf("Marshal jsonBig") | |
| diff(t, b, jsonBig) | |
| return | |
| } | |
| } | |
| 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.Compare(s0, s1) != 0 { | |
| 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>` | |
| const expected = `"\"foobar\"\u003chtml\u003e"` | |
| b, err := Marshal(input) | |
| if err != nil { | |
| t.Fatalf("Marshal error: %v", err) | |
| } | |
| if s := string(b); s != expected { | |
| t.Errorf("Encoding of [%s] was [%s], want [%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`}, | |
| } | |
| // 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(c) { | |
| 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 | |
| } | |
| want := S{ | |
| R0: 12, | |
| R1: new(Ref), | |
| } | |
| *want.R1 = 12 | |
| var got S | |
| if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref"}`), &got); err != nil { | |
| t.Fatalf("Unmarshal: %v", err) | |
| } | |
| if !reflect.DeepEqual(got, want) { | |
| t.Errorf("got %+v, want %+v", got, want) | |
| } | |
| } | |
| // Test that anonymous fields are ignored. | |
| // We may assign meaning to them later. | |
| func TestAnonymous(t *testing.T) { | |
| type S struct { | |
| T | |
| N int | |
| } | |
| data, err := Marshal(new(S)) | |
| if err != nil { | |
| t.Fatalf("Marshal: %v", err) | |
| } | |
| want := `{"N":0}` | |
| if string(data) != want { | |
| t.Fatalf("Marshal = %#q, want %#q", string(data), want) | |
| } | |
| var s S | |
| if err := Unmarshal([]byte(`{"T": 1, "T": {"Y": 1}, "N": 2}`), &s); err != nil { | |
| t.Fatalf("Unmarshal: %v", err) | |
| } | |
| if s.N != 2 { | |
| t.Fatal("Unmarshal: did not set N") | |
| } | |
| if s.T.Y != 0 { | |
| t.Fatal("Unmarshal: did set T.Y") | |
| } | |
| } | |
| // 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") | |
| } | |
| } | |
| 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) | |
| } | |
| } | |
| } |