| // Copyright 2011 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" |
| "math" |
| "reflect" |
| "testing" |
| ) |
| |
| type Optionals struct { |
| Sr string `json:"sr"` |
| So string `json:"so,omitempty"` |
| Sw string `json:"-"` |
| |
| Ir int `json:"omitempty"` // actually named omitempty, not an option |
| Io int `json:"io,omitempty"` |
| |
| Slr []string `json:"slr,random"` |
| Slo []string `json:"slo,omitempty"` |
| |
| Mr map[string]interface{} `json:"mr"` |
| Mo map[string]interface{} `json:",omitempty"` |
| } |
| |
| var optionalsExpected = `{ |
| "sr": "", |
| "omitempty": 0, |
| "slr": null, |
| "mr": {} |
| }` |
| |
| func TestOmitEmpty(t *testing.T) { |
| var o Optionals |
| o.Sw = "something" |
| o.Mr = map[string]interface{}{} |
| o.Mo = map[string]interface{}{} |
| |
| got, err := MarshalIndent(&o, "", " ") |
| if err != nil { |
| t.Fatal(err) |
| } |
| if got := string(got); got != optionalsExpected { |
| t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected) |
| } |
| } |
| |
| type StringTag struct { |
| BoolStr bool `json:",string"` |
| IntStr int64 `json:",string"` |
| StrStr string `json:",string"` |
| } |
| |
| var stringTagExpected = `{ |
| "BoolStr": "true", |
| "IntStr": "42", |
| "StrStr": "\"xzbit\"" |
| }` |
| |
| func TestStringTag(t *testing.T) { |
| var s StringTag |
| s.BoolStr = true |
| s.IntStr = 42 |
| s.StrStr = "xzbit" |
| got, err := MarshalIndent(&s, "", " ") |
| if err != nil { |
| t.Fatal(err) |
| } |
| if got := string(got); got != stringTagExpected { |
| t.Fatalf(" got: %s\nwant: %s\n", got, stringTagExpected) |
| } |
| |
| // Verify that it round-trips. |
| var s2 StringTag |
| err = NewDecoder(bytes.NewBuffer(got)).Decode(&s2) |
| if err != nil { |
| t.Fatalf("Decode: %v", err) |
| } |
| if !reflect.DeepEqual(s, s2) { |
| t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", s, string(got), s2) |
| } |
| } |
| |
| // byte slices are special even if they're renamed types. |
| type renamedByte byte |
| type renamedByteSlice []byte |
| type renamedRenamedByteSlice []renamedByte |
| |
| func TestEncodeRenamedByteSlice(t *testing.T) { |
| s := renamedByteSlice("abc") |
| result, err := Marshal(s) |
| if err != nil { |
| t.Fatal(err) |
| } |
| expect := `"YWJj"` |
| if string(result) != expect { |
| t.Errorf(" got %s want %s", result, expect) |
| } |
| r := renamedRenamedByteSlice("abc") |
| result, err = Marshal(r) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if string(result) != expect { |
| t.Errorf(" got %s want %s", result, expect) |
| } |
| } |
| |
| var unsupportedValues = []interface{}{ |
| math.NaN(), |
| math.Inf(-1), |
| math.Inf(1), |
| } |
| |
| func TestUnsupportedValues(t *testing.T) { |
| for _, v := range unsupportedValues { |
| if _, err := Marshal(v); err != nil { |
| if _, ok := err.(*UnsupportedValueError); !ok { |
| t.Errorf("for %v, got %T want UnsupportedValueError", v, err) |
| } |
| } else { |
| t.Errorf("for %v, expected error", v) |
| } |
| } |
| } |
| |
| // Ref has Marshaler and Unmarshaler methods with pointer receiver. |
| type Ref int |
| |
| func (*Ref) MarshalJSON() ([]byte, error) { |
| return []byte(`"ref"`), nil |
| } |
| |
| func (r *Ref) UnmarshalJSON([]byte) error { |
| *r = 12 |
| return nil |
| } |
| |
| // Val has Marshaler methods with value receiver. |
| type Val int |
| |
| func (Val) MarshalJSON() ([]byte, error) { |
| return []byte(`"val"`), nil |
| } |
| |
| func TestRefValMarshal(t *testing.T) { |
| var s = struct { |
| R0 Ref |
| R1 *Ref |
| V0 Val |
| V1 *Val |
| }{ |
| R0: 12, |
| R1: new(Ref), |
| V0: 13, |
| V1: new(Val), |
| } |
| const want = `{"R0":"ref","R1":"ref","V0":"val","V1":"val"}` |
| b, err := Marshal(&s) |
| if err != nil { |
| t.Fatalf("Marshal: %v", err) |
| } |
| if got := string(b); got != want { |
| t.Errorf("got %q, want %q", got, want) |
| } |
| } |
| |
| // C implements Marshaler and returns unescaped JSON. |
| type C int |
| |
| func (C) MarshalJSON() ([]byte, error) { |
| return []byte(`"<&>"`), nil |
| } |
| |
| func TestMarshalerEscaping(t *testing.T) { |
| var c C |
| const want = `"\u003c\u0026\u003e"` |
| b, err := Marshal(c) |
| if err != nil { |
| t.Fatalf("Marshal: %v", err) |
| } |
| if got := string(b); got != want { |
| t.Errorf("got %q, want %q", got, want) |
| } |
| } |
| |
| type IntType int |
| |
| type MyStruct struct { |
| IntType |
| } |
| |
| func TestAnonymousNonstruct(t *testing.T) { |
| var i IntType = 11 |
| a := MyStruct{i} |
| const want = `{"IntType":11}` |
| |
| b, err := Marshal(a) |
| if err != nil { |
| t.Fatalf("Marshal: %v", err) |
| } |
| if got := string(b); got != want { |
| t.Errorf("got %q, want %q", got, want) |
| } |
| } |
| |
| type BugA struct { |
| S string |
| } |
| |
| type BugB struct { |
| BugA |
| S string |
| } |
| |
| type BugC struct { |
| S string |
| } |
| |
| // Legal Go: We never use the repeated embedded field (S). |
| type BugX struct { |
| A int |
| BugA |
| BugB |
| } |
| |
| // Issue 5245. |
| func TestEmbeddedBug(t *testing.T) { |
| v := BugB{ |
| BugA{"A"}, |
| "B", |
| } |
| b, err := Marshal(v) |
| if err != nil { |
| t.Fatal("Marshal:", err) |
| } |
| want := `{"S":"B"}` |
| got := string(b) |
| if got != want { |
| t.Fatalf("Marshal: got %s want %s", got, want) |
| } |
| // Now check that the duplicate field, S, does not appear. |
| x := BugX{ |
| A: 23, |
| } |
| b, err = Marshal(x) |
| if err != nil { |
| t.Fatal("Marshal:", err) |
| } |
| want = `{"A":23}` |
| got = string(b) |
| if got != want { |
| t.Fatalf("Marshal: got %s want %s", got, want) |
| } |
| } |
| |
| type BugD struct { // Same as BugA after tagging. |
| XXX string `json:"S"` |
| } |
| |
| // BugD's tagged S field should dominate BugA's. |
| type BugY struct { |
| BugA |
| BugD |
| } |
| |
| // Test that a field with a tag dominates untagged fields. |
| func TestTaggedFieldDominates(t *testing.T) { |
| v := BugY{ |
| BugA{"BugA"}, |
| BugD{"BugD"}, |
| } |
| b, err := Marshal(v) |
| if err != nil { |
| t.Fatal("Marshal:", err) |
| } |
| want := `{"S":"BugD"}` |
| got := string(b) |
| if got != want { |
| t.Fatalf("Marshal: got %s want %s", got, want) |
| } |
| } |
| |
| // There are no tags here, so S should not appear. |
| type BugZ struct { |
| BugA |
| BugC |
| BugY // Contains a tagged S field through BugD; should not dominate. |
| } |
| |
| func TestDuplicatedFieldDisappears(t *testing.T) { |
| v := BugZ{ |
| BugA{"BugA"}, |
| BugC{"BugC"}, |
| BugY{ |
| BugA{"nested BugA"}, |
| BugD{"nested BugD"}, |
| }, |
| } |
| b, err := Marshal(v) |
| if err != nil { |
| t.Fatal("Marshal:", err) |
| } |
| want := `{}` |
| got := string(b) |
| if got != want { |
| t.Fatalf("Marshal: got %s want %s", got, want) |
| } |
| } |