// 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" "fmt" "log" "math" "reflect" "regexp" "strconv" "testing" "unicode" ) 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"` //nolint:staticcheck // It's intentionally wrong. Slo []string `json:"slo,omitempty"` Mr map[string]any `json:"mr"` Mo map[string]any `json:",omitempty"` Fr float64 `json:"fr"` Fo float64 `json:"fo,omitempty"` Br bool `json:"br"` Bo bool `json:"bo,omitempty"` Ur uint `json:"ur"` Uo uint `json:"uo,omitempty"` Str struct{} `json:"str"` Sto struct{} `json:"sto,omitempty"` } var optionalsExpected = `{ "sr": "", "omitempty": 0, "slr": null, "mr": {}, "fr": 0, "br": false, "ur": 0, "str": {}, "sto": {} }` func TestOmitEmpty(t *testing.T) { var o Optionals o.Sw = "something" o.Mr = map[string]any{} o.Mo = map[string]any{} 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": "\u0022xzbit\u0022" }` 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.NewReader(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 = []any{ 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 { //nolint:errorlint // It must match exactly, it's a test. 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 } // RefText has Marshaler and Unmarshaler methods with pointer receiver. type RefText int func (*RefText) MarshalText() ([]byte, error) { return []byte(`"ref"`), nil } func (r *RefText) UnmarshalText([]byte) error { *r = 13 return nil } // ValText has Marshaler methods with value receiver. type ValText int func (ValText) MarshalText() ([]byte, error) { return []byte(`"val"`), nil } func TestRefValMarshal(t *testing.T) { var s = struct { R0 Ref R1 *Ref R2 RefText R3 *RefText V0 Val V1 *Val V2 ValText V3 *ValText }{ R0: 12, R1: new(Ref), R2: 14, R3: new(RefText), V0: 13, V1: new(Val), V2: 15, V3: new(ValText), } const want = `{"R0":"ref","R1":"ref","R2":"\u0022ref\u0022","R3":"\u0022ref\u0022","V0":"val","V1":"val","V2":"\u0022val\u0022","V3":"\u0022val\u0022"}` 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 } // CText implements Marshaler and returns unescaped text. type CText int func (CText) MarshalText() ([]byte, error) { return []byte(`"<&>"`), nil } func TestMarshaler_NeoGo_PR2174(t *testing.T) { source := "IOU(欠条币):一种支持负数的NEP-17(非严格意义上的)资产,合约无存储区,账户由区块链浏览器统计" b, err := Marshal(source) if err != nil { t.Fatalf("Marshal(c): %v", err) } want := `"` + `IOU\uFF08\u6B20\u6761\u5E01\uFF09\uFF1A\u4E00\u79CD\u652F\u6301\u8D1F\u6570\u7684NEP-17\uFF08\u975E\u4E25\u683C\u610F\u4E49\u4E0A\u7684\uFF09\u8D44\u4EA7\uFF0C\u5408\u7EA6\u65E0\u5B58\u50A8\u533A\uFF0C\u8D26\u6237\u7531\u533A\u5757\u94FE\u6D4F\u89C8\u5668\u7EDF\u8BA1` + `"` if got := string(b); got != want { t.Errorf("Marshal(c) = %#q, want %#q", got, want) } } func TestMarshalerEscaping(t *testing.T) { var c C want := `"\u003C\u0026\u003E"` b, err := Marshal(c) if err != nil { t.Fatalf("Marshal(c): %v", err) } if got := string(b); got != want { t.Errorf("Marshal(c) = %#q, want %#q", got, want) } var ct CText want = `"\u0022\u003C\u0026\u003E\u0022"` b, err = Marshal(ct) if err != nil { t.Fatalf("Marshal(ct): %v", err) } if got := string(b); got != want { t.Errorf("Marshal(ct) = %#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 unexportedIntType int type MyStructWithUnexportedIntType struct { unexportedIntType } func TestAnonymousNonstructWithUnexportedType(t *testing.T) { a := MyStructWithUnexportedIntType{11} const want = `{}` 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 MyStructContainingUnexportedStruct struct { unexportedStructType1 unexportedIntType } type unexportedStructType1 struct { ExportedIntType1 unexportedIntType unexportedStructType2 } type unexportedStructType2 struct { ExportedIntType2 unexportedIntType } type ExportedIntType1 int type ExportedIntType2 int func TestUnexportedAnonymousStructWithExportedType(t *testing.T) { s2 := unexportedStructType2{3, 4} s1 := unexportedStructType1{1, 2, s2} a := MyStructContainingUnexportedStruct{s1, 6} const want = `{"ExportedIntType1":1,"ExportedIntType2":3}` 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 16042. Even if a nil interface value is passed in // as long as it implements MarshalJSON, it should be marshaled. type nilMarshaler string func (nm *nilMarshaler) MarshalJSON() ([]byte, error) { if nm == nil { return Marshal("0zenil0") } return Marshal("zenil:" + string(*nm)) } // Issue 16042. func TestNilMarshal(t *testing.T) { testCases := []struct { v any want string }{ {v: nil, want: `null`}, {v: new(float64), want: `0`}, {v: []any(nil), want: `null`}, {v: []string(nil), want: `null`}, {v: map[string]string(nil), want: `null`}, {v: []byte(nil), want: `null`}, {v: struct{ M string }{"gopher"}, want: `{"M":"gopher"}`}, {v: struct{ M Marshaler }{}, want: `{"M":null}`}, {v: struct{ M Marshaler }{(*nilMarshaler)(nil)}, want: `{"M":"0zenil0"}`}, {v: struct{ M any }{(*nilMarshaler)(nil)}, want: `{"M":null}`}, } for _, tt := range testCases { out, err := Marshal(tt.v) if err != nil || string(out) != tt.want { t.Errorf("Marshal(%#v) = %#q, %#v, want %#q, nil", tt.v, out, err, tt.want) continue } } } // 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) } } func TestStringBytes(t *testing.T) { t.Parallel() // Test that encodeState.stringBytes and encodeState.string use the same encoding. var r []rune for i := '\u0000'; i <= unicode.MaxRune; i++ { r = append(r, i) } s := string(r) + "\xff\xff\xffhello" // some invalid UTF-8 too for _, escapeHTML := range []bool{true, false} { es := &encodeState{} es.string(s, escapeHTML) esBytes := &encodeState{} esBytes.stringBytes([]byte(s), escapeHTML) enc := es.String() encBytes := esBytes.String() if enc != encBytes { i := 0 for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] { i++ } enc = enc[i:] encBytes = encBytes[i:] i = 0 for i < len(enc) && i < len(encBytes) && enc[len(enc)-i-1] == encBytes[len(encBytes)-i-1] { i++ } enc = enc[:len(enc)-i] encBytes = encBytes[:len(encBytes)-i] if len(enc) > 20 { enc = enc[:20] + "..." } if len(encBytes) > 20 { encBytes = encBytes[:20] + "..." } t.Errorf("with escapeHTML=%t, encodings differ at %#q vs %#q", escapeHTML, enc, encBytes) } } } func TestIssue10281(t *testing.T) { type Foo struct { N Number } x := Foo{Number(`invalid`)} b, err := Marshal(&x) if err == nil { t.Errorf("Marshal(&x) = %#q; want error", b) } } func TestHTMLEscape(t *testing.T) { var b, want bytes.Buffer m := `{"M":"foo &` + "\xe2\x80\xa8 \xe2\x80\xa9" + `"}` want.Write([]byte(`{"M":"\u003Chtml\u003Efoo \u0026\u2028 \u2029\u003C/html\u003E"}`)) HTMLEscape(&b, []byte(m)) if !bytes.Equal(b.Bytes(), want.Bytes()) { t.Errorf("HTMLEscape(&b, []byte(m)) = %s; want %s", b.Bytes(), want.Bytes()) } } // golang.org/issue/8582. func TestEncodePointerString(t *testing.T) { type stringPointer struct { N *int64 `json:"n,string"` } var n int64 = 42 b, err := Marshal(stringPointer{N: &n}) if err != nil { t.Fatalf("Marshal: %v", err) } if got, want := string(b), `{"n":"42"}`; got != want { t.Errorf("Marshal = %s, want %s", got, want) } var back stringPointer err = Unmarshal(b, &back) if err != nil { t.Fatalf("Unmarshal: %v", err) } if back.N == nil { t.Fatalf("Unmarshaled nil N field") } if *back.N != 42 { t.Fatalf("*N = %d; want 42", *back.N) } } var encodeStringTests = []struct { in string out string }{ {"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", `"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000B\f\r\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F"`}, {"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f", `" !\u0022#$%\u0026\u0027()*\u002B,-./0123456789:;\u003C=\u003E?"`}, {"\x40\x41\x44\x45\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x54\x55\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f", `"@ADEDEFGHIJKLMNOPQTUTUVWXYZ[\\]^_"`}, {"\x60\x61\x66\x67\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x76\x77\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f", `"\u0060afgdefghijklmnopqvwtuvwxyz{|}~\u007F"`}, {"\x80\x81\x88\x89\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x98\x99\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f", `"\u0080\u0081\u0088\u0089\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0098\u0099\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F"`}, {"\xa0\xa1\xaa\xab\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xba\xbb\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf", `"\u00A0\u00A1\u00AA\u00AB\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB\u00AC\u00AD\u00AE\u00AF\u00B0\u00B1\u00BA\u00BB\u00B4\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF"`}, {"\xc0\xc1\xcc\xcd\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xdc\xdd\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf", `"\u00C0\u00C1\u00CC\u00CD\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00DC\u00DD\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF"`}, {"\xe0\xe1\xee\xef\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xfe\xff\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", `"\u00E0\u00E1\u00EE\u00EF\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00FE\u00FF\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF"`}, {"\xff\xd0", `"\u00FF\u00D0"`}, {"测试", `"\u6D4B\u8BD5"`}, } func TestEncodeString(t *testing.T) { for _, tt := range encodeStringTests { b, err := Marshal(tt.in) if err != nil { t.Errorf("Marshal(%q): %v", tt.in, err) continue } out := string(b) if out != tt.out { t.Errorf("Marshal(%q) = %#q, want %#q", tt.in, out, tt.out) } } } type jsonbyte byte func (b jsonbyte) MarshalJSON() ([]byte, error) { return tenc(`{"JB":%d}`, b) } type textbyte byte func (b textbyte) MarshalText() ([]byte, error) { return tenc(`TB:%d`, b) } type jsonint int func (i jsonint) MarshalJSON() ([]byte, error) { return tenc(`{"JI":%d}`, i) } type textint int func (i textint) MarshalText() ([]byte, error) { return tenc(`TI:%d`, i) } func tenc(format string, a ...any) ([]byte, error) { var buf bytes.Buffer fmt.Fprintf(&buf, format, a...) return buf.Bytes(), nil } // Issue 13783. func TestEncodeBytekind(t *testing.T) { testdata := []struct { data any want string }{ {byte(7), "7"}, {jsonbyte(7), `{"JB":7}`}, {textbyte(4), `"TB:4"`}, {jsonint(5), `{"JI":5}`}, {textint(1), `"TI:1"`}, {[]byte{0, 1}, `"AAE="`}, {[]jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`}, {[][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`}, {[]textbyte{2, 3}, `["TB:2","TB:3"]`}, {[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`}, {[]textint{9, 3}, `["TI:9","TI:3"]`}, {[]int{9, 3}, `[9,3]`}, } for _, d := range testdata { js, err := Marshal(d.data) if err != nil { t.Error(err) continue } got, want := string(js), d.want if got != want { t.Errorf("got %s, want %s", got, want) } } } func TestTextMarshalerMapKeysAreSorted(t *testing.T) { b, err := Marshal(map[unmarshalerText]int{ {"x", "y"}: 1, {"y", "x"}: 2, {"a", "z"}: 3, {"z", "a"}: 4, }) if err != nil { t.Fatalf("Failed to Marshal text.Marshaler: %v", err) } const want = `{"a:z":3,"x:y":1,"y:x":2,"z:a":4}` if string(b) != want { t.Errorf("Marshal map with text.Marshaler keys: got %#q, want %#q", b, want) } } var re = regexp.MustCompile // syntactic checks on form of marshaled floating point numbers. var badFloatREs = []*regexp.Regexp{ re(`p`), // no binary exponential notation re(`^\+`), // no leading + sign re(`^-?0[^.]`), // no unnecessary leading zeros re(`^-?\.`), // leading zero required before decimal point re(`\.(e|$)`), // no trailing decimal re(`\.[0-9]+0(e|$)`), // no trailing zero in fraction re(`^-?(0|[0-9]{2,})\..*e`), // exponential notation must have normalized mantissa re(`e[0-9]`), // positive exponent must be signed re(`e[+-]0`), // exponent must not have leading zeros re(`e-[1-6]$`), // not tiny enough for exponential notation re(`e+(.|1.|20)$`), // not big enough for exponential notation re(`^-?0\.0000000`), // too tiny, should use exponential notation re(`^-?[0-9]{22}`), // too big, should use exponential notation re(`[1-9][0-9]{16}[1-9]`), // too many significant digits in integer re(`[1-9][0-9.]{17}[1-9]`), // too many significant digits in decimal // below here for float32 only re(`[1-9][0-9]{8}[1-9]`), // too many significant digits in integer re(`[1-9][0-9.]{9}[1-9]`), // too many significant digits in decimal } func TestMarshalFloat(t *testing.T) { t.Parallel() nfail := 0 test := func(f float64, bits int) { vf := any(f) if bits == 32 { f = float64(float32(f)) // round vf = float32(f) } bout, err := Marshal(vf) if err != nil { t.Errorf("Marshal(%T(%g)): %v", vf, vf, err) nfail++ return } out := string(bout) // result must convert back to the same float g, err := strconv.ParseFloat(out, bits) if err != nil { t.Errorf("Marshal(%T(%g)) = %q, cannot parse back: %v", vf, vf, out, err) nfail++ return } if f != g || fmt.Sprint(f) != fmt.Sprint(g) { // fmt.Sprint handles ±0 t.Errorf("Marshal(%T(%g)) = %q (is %g, not %g)", vf, vf, out, float32(g), vf) nfail++ return } bad := badFloatREs if bits == 64 { bad = bad[:len(bad)-2] } for _, re := range bad { if re.MatchString(out) { t.Errorf("Marshal(%T(%g)) = %q, must not match /%s/", vf, vf, out, re) nfail++ return } } } var ( bigger = math.Inf(+1) smaller = math.Inf(-1) ) var digits = "1.2345678901234567890123" for i := len(digits); i >= 2; i-- { for exp := -30; exp <= 30; exp++ { for _, sign := range "+-" { for bits := 32; bits <= 64; bits += 32 { s := fmt.Sprintf("%c%se%d", sign, digits[:i], exp) f, err := strconv.ParseFloat(s, bits) if err != nil { log.Fatal(err) } next := math.Nextafter if bits == 32 { next = func(g, h float64) float64 { return float64(math.Nextafter32(float32(g), float32(h))) } } test(f, bits) test(next(f, bigger), bits) test(next(f, smaller), bits) if nfail > 50 { t.Fatalf("stopping test early") } } } } } test(0, 64) test(math.Copysign(0, -1), 64) test(0, 32) test(math.Copysign(0, -1), 32) } func TestMarshalRawMessageValue(t *testing.T) { type ( T1 struct { M RawMessage `json:",omitempty"` } T2 struct { M *RawMessage `json:",omitempty"` } ) var ( rawNil = RawMessage(nil) rawEmpty = RawMessage([]byte{}) rawText = RawMessage([]byte(`"foo"`)) ) tests := []struct { in any want string ok bool }{ // Test with nil RawMessage. {rawNil, "null", true}, {&rawNil, "null", true}, {[]any{rawNil}, "[null]", true}, {&[]any{rawNil}, "[null]", true}, {[]any{&rawNil}, "[null]", true}, {&[]any{&rawNil}, "[null]", true}, {struct{ M RawMessage }{rawNil}, `{"M":null}`, true}, {&struct{ M RawMessage }{rawNil}, `{"M":null}`, true}, {struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true}, {&struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true}, {map[string]any{"M": rawNil}, `{"M":null}`, true}, {&map[string]any{"M": rawNil}, `{"M":null}`, true}, {map[string]any{"M": &rawNil}, `{"M":null}`, true}, {&map[string]any{"M": &rawNil}, `{"M":null}`, true}, {T1{rawNil}, "{}", true}, {T2{&rawNil}, `{"M":null}`, true}, {&T1{rawNil}, "{}", true}, {&T2{&rawNil}, `{"M":null}`, true}, // Test with empty, but non-nil, RawMessage. {rawEmpty, "", false}, {&rawEmpty, "", false}, {[]any{rawEmpty}, "", false}, {&[]any{rawEmpty}, "", false}, {[]any{&rawEmpty}, "", false}, {&[]any{&rawEmpty}, "", false}, {struct{ X RawMessage }{rawEmpty}, "", false}, {&struct{ X RawMessage }{rawEmpty}, "", false}, {struct{ X *RawMessage }{&rawEmpty}, "", false}, {&struct{ X *RawMessage }{&rawEmpty}, "", false}, {map[string]any{"nil": rawEmpty}, "", false}, {&map[string]any{"nil": rawEmpty}, "", false}, {map[string]any{"nil": &rawEmpty}, "", false}, {&map[string]any{"nil": &rawEmpty}, "", false}, {T1{rawEmpty}, "{}", true}, {T2{&rawEmpty}, "", false}, {&T1{rawEmpty}, "{}", true}, {&T2{&rawEmpty}, "", false}, // Test with RawMessage with some text. // // The tests below marked with Issue6458 used to generate "ImZvbyI=" instead "foo". // This behavior was intentionally changed in Go 1.8. // See https://github.com/golang/go/issues/14493#issuecomment-255857318 {rawText, `"foo"`, true}, // Issue6458 {&rawText, `"foo"`, true}, {[]any{rawText}, `["foo"]`, true}, // Issue6458 {&[]any{rawText}, `["foo"]`, true}, // Issue6458 {[]any{&rawText}, `["foo"]`, true}, {&[]any{&rawText}, `["foo"]`, true}, {struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458 {&struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true}, {struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true}, {&struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true}, {map[string]any{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 {&map[string]any{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 {map[string]any{"M": &rawText}, `{"M":"foo"}`, true}, {&map[string]any{"M": &rawText}, `{"M":"foo"}`, true}, {T1{rawText}, `{"M":"foo"}`, true}, // Issue6458 {T2{&rawText}, `{"M":"foo"}`, true}, {&T1{rawText}, `{"M":"foo"}`, true}, {&T2{&rawText}, `{"M":"foo"}`, true}, } for i, tt := range tests { b, err := Marshal(tt.in) if ok := (err == nil); ok != tt.ok { if err != nil { t.Errorf("test %d, unexpected failure: %v", i, err) } else { t.Errorf("test %d, unexpected success", i) } } if got := string(b); got != tt.want { t.Errorf("test %d, Marshal(%#v) = %q, want %q", i, tt.in, got, tt.want) } } } type ObjEnc struct { A int B OrderedObject C any } var orderedObjectTests = []struct { in any out string }{ {OrderedObject{}, `{}`}, {OrderedObject{Member{"A", []int{1, 2, 3}}, Member{"B", 23}, Member{"C", "C"}}, `{"A":[1,2,3],"B":23,"C":"C"}`}, {ObjEnc{A: 234, B: OrderedObject{Member{"K", "V"}, Member{"V", 4}}}, `{"A":234,"B":{"K":"V","V":4},"C":null}`}, {ObjEnc{A: 234, B: OrderedObject{}, C: OrderedObject{{"A", 0}}}, `{"A":234,"B":{},"C":{"A":0}}`}, {[]OrderedObject{{{"A", "Ay"}, {"B", "Bee"}}, {{"A", "Nay"}}}, `[{"A":"Ay","B":"Bee"},{"A":"Nay"}]`}, {map[string]OrderedObject{"A": {{"A", "Ay"}, {"B", "Bee"}}}, `{"A":{"A":"Ay","B":"Bee"}}`}, {map[string]any{"A": OrderedObject{{"A", "Ay"}, {"B", "Bee"}}}, `{"A":{"A":"Ay","B":"Bee"}}`}, } func TestEncodeOrderedObject(t *testing.T) { for i, o := range orderedObjectTests { d, err := Marshal(o.in) if err != nil { t.Errorf("Unexpected error %v", err) continue } ds := string(d) if o.out != ds { t.Errorf("#%d expected '%v', was '%v'", i, o.out, ds) } } }