Newer
Older
}
}
// Custom types with []byte as underlying type could not be marshaled
// and then unmarshaled.
// Issue 8962.
func TestByteKind(t *testing.T) {
type byteKind []byte
want := byteKind("hello")
data, err := Marshal(want)
if err != nil {
}
var got byteKind
err = Unmarshal(data, &got)
if err != nil {
}
if !slices.Equal(got, want) {
t.Fatalf("Marshal/Unmarshal mismatch:\n\tgot: %v\n\twant: %v", got, want)
}
}
// The fix for issue 8962 introduced a regression.
// Issue 12921.
func TestSliceOfCustomByte(t *testing.T) {
type Uint8 uint8
want := []Uint8("hello")
data, err := Marshal(want)
if err != nil {
var got []Uint8
err = Unmarshal(data, &got)
if err != nil {
if !slices.Equal(got, want) {
t.Fatalf("Marshal/Unmarshal mismatch:\n\tgot: %v\n\twant: %v", got, want)
func TestUnmarshalTypeError(t *testing.T) {
tests := []struct {
CaseName
dest any
in string
}{
{Name(""), new(string), `{"user": "name"}`}, // issue 4628.
{Name(""), new(error), `{}`}, // issue 4222
{Name(""), new(error), `[]`},
{Name(""), new(error), `""`},
{Name(""), new(error), `123`},
{Name(""), new(error), `true`},
}
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
err := Unmarshal([]byte(tt.in), tt.dest)
if _, ok := err.(*UnmarshalTypeError); !ok {
t.Errorf("%s: Unmarshal(%#q, %T):\n\tgot: %T\n\twant: %T",
tt.Where, tt.in, tt.dest, err, new(UnmarshalTypeError))
}
})
func TestUnmarshalSyntax(t *testing.T) {
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
tests := []struct {
CaseName
in string
}{
{Name(""), "tru"},
{Name(""), "fals"},
{Name(""), "nul"},
{Name(""), "123e"},
{Name(""), `"hello`},
{Name(""), `[1,2,3`},
{Name(""), `{"key":1`},
{Name(""), `{"key":1,`},
}
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
err := Unmarshal([]byte(tt.in), &x)
if _, ok := err.(*SyntaxError); !ok {
t.Errorf("%s: Unmarshal(%#q, any):\n\tgot: %T\n\twant: %T",
tt.Where, tt.in, err, new(SyntaxError))
}
})
// Test handling of unexported fields that should be ignored.
// Issue 4660
type unexportedFields struct {
Name string
m map[string]any `json:"-"`
m2 map[string]any `json:"abcd"`
s []int `json:"-"`
}
func TestUnmarshalUnexported(t *testing.T) {
input := `{"Name": "Bob", "m": {"x": 123}, "m2": {"y": 456}, "abcd": {"z": 789}, "s": [2, 3]}`
want := &unexportedFields{Name: "Bob"}
out := &unexportedFields{}
err := Unmarshal([]byte(input), out)
if err != nil {
}
if !reflect.DeepEqual(out, want) {
t.Errorf("Unmarshal:\n\tgot: %+v\n\twant: %+v", 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) {
switch err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3); {
case err == nil:
t.Fatalf("Unmarshal error: got nil, want non-nil")
case !strings.Contains(err.Error(), "range"):
t.Errorf("Unmarshal error:\n\tgot: %v\n\twant: out of range", 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 := `[{}]`
err := Unmarshal([]byte(json), &dest)
if err != nil {
}
}
// Test semantics of pre-filled data, such as struct fields, map elements,
// slices, and arrays.
// Issues 4900 and 8837, among others.
func TestPrefilled(t *testing.T) {
// Values here change, cannot reuse table across runs.
in string
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
}{{
CaseName: Name(""),
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},
}, {
CaseName: Name(""),
in: `{"X": 1, "Y": 2}`,
ptr: &map[string]any{"X": float32(3), "Y": int16(4), "Z": 1.5},
out: &map[string]any{"X": float64(1), "Y": float64(2), "Z": 1.5},
}, {
CaseName: Name(""),
in: `[2]`,
ptr: &[]int{1},
out: &[]int{2},
}, {
CaseName: Name(""),
in: `[2, 3]`,
ptr: &[]int{1},
out: &[]int{2, 3},
}, {
CaseName: Name(""),
in: `[2, 3]`,
ptr: &[...]int{1},
out: &[...]int{2},
}, {
CaseName: Name(""),
in: `[3]`,
ptr: &[...]int{1, 2},
out: &[...]int{3, 0},
}}
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
ptrstr := fmt.Sprintf("%v", tt.ptr)
err := Unmarshal([]byte(tt.in), tt.ptr) // tt.ptr edited here
if err != nil {
t.Errorf("%s: Unmarshal error: %v", tt.Where, err)
}
if !reflect.DeepEqual(tt.ptr, tt.out) {
t.Errorf("%s: Unmarshal(%#q, %T):\n\tgot: %v\n\twant: %v", tt.Where, tt.in, ptrstr, tt.ptr, tt.out)
}
})
}
}
func TestInvalidUnmarshal(t *testing.T) {
buf := []byte(`{"a":"1"}`)
tests := []struct {
CaseName
v any
want string
}{
{Name(""), nil, "json: Unmarshal(nil)"},
{Name(""), struct{}{}, "json: Unmarshal(non-pointer struct {})"},
{Name(""), (*int)(nil), "json: Unmarshal(nil *int)"},
}
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
err := Unmarshal(buf, tt.v)
if err == nil {
t.Fatalf("%s: Unmarshal error: got nil, want non-nil", tt.Where)
}
if got := err.Error(); got != tt.want {
t.Errorf("%s: Unmarshal error:\n\tgot: %s\n\twant: %s", tt.Where, got, tt.want)
}
})
Russ Cox
committed
func TestInvalidUnmarshalText(t *testing.T) {
buf := []byte(`123`)
tests := []struct {
CaseName
v any
want string
}{
{Name(""), nil, "json: Unmarshal(nil)"},
{Name(""), struct{}{}, "json: Unmarshal(non-pointer struct {})"},
{Name(""), (*int)(nil), "json: Unmarshal(nil *int)"},
{Name(""), new(net.IP), "json: cannot unmarshal number into Go value of type *net.IP"},
}
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
err := Unmarshal(buf, tt.v)
if err == nil {
t.Fatalf("%s: Unmarshal error: got nil, want non-nil", tt.Where)
}
if got := err.Error(); got != tt.want {
t.Errorf("%s: Unmarshal error:\n\tgot: %s\n\twant: %s", tt.Where, got, tt.want)
}
})
Russ Cox
committed
}
}
// 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"`
P *int `json:",string"`
}{M: make(map[string]string), S: make([]string, 0), I: num, P: &num}
data, err := Marshal(item)
if err != nil {
}
err = Unmarshal(data, &item)
if err != nil {
Joe Tsai
committed
// Test unmarshal behavior with regards to embedded unexported structs.
//
// (Issue 21357) If the embedded struct is a pointer and is unallocated,
// this returns an error because unmarshal cannot set the field.
//
// (Issue 24152) If the embedded struct is given an explicit name,
// ensure that the normal unmarshal logic does not panic in reflect.
//
// (Issue 28145) If the embedded struct is given an explicit name and has
// exported methods, don't cause a panic trying to get its value.
func TestUnmarshalEmbeddedUnexported(t *testing.T) {
Joe Tsai
committed
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
type (
embed1 struct{ Q int }
embed2 struct{ Q int }
embed3 struct {
Q int64 `json:",string"`
}
S1 struct {
*embed1
R int
}
S2 struct {
*embed1
Q int
}
S3 struct {
embed1
R int
}
S4 struct {
*embed1
embed2
}
S5 struct {
*embed3
R int
}
S6 struct {
embed1 `json:"embed1"`
}
S7 struct {
embed1 `json:"embed1"`
embed2
}
S8 struct {
embed1 `json:"embed1"`
embed2 `json:"embed2"`
Q int
}
S9 struct {
unexportedWithMethods `json:"embed"`
}
Joe Tsai
committed
)
tests := []struct {
Joe Tsai
committed
in string
Joe Tsai
committed
err error
}{{
// Error since we cannot set S1.embed1, but still able to set S1.R.
CaseName: Name(""),
in: `{"R":2,"Q":1}`,
ptr: new(S1),
out: &S1{R: 2},
err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed1"),
Joe Tsai
committed
}, {
// The top level Q field takes precedence.
CaseName: Name(""),
in: `{"Q":1}`,
ptr: new(S2),
out: &S2{Q: 1},
Joe Tsai
committed
}, {
// No issue with non-pointer variant.
CaseName: Name(""),
in: `{"R":2,"Q":1}`,
ptr: new(S3),
out: &S3{embed1: embed1{Q: 1}, R: 2},
Joe Tsai
committed
}, {
// No error since both embedded structs have field R, which annihilate each other.
// Thus, no attempt is made at setting S4.embed1.
CaseName: Name(""),
in: `{"R":2}`,
ptr: new(S4),
out: new(S4),
Joe Tsai
committed
}, {
// Error since we cannot set S5.embed1, but still able to set S5.R.
CaseName: Name(""),
in: `{"R":2,"Q":1}`,
ptr: new(S5),
out: &S5{R: 2},
err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed3"),
}, {
// Issue 24152, ensure decodeState.indirect does not panic.
CaseName: Name(""),
in: `{"embed1": {"Q": 1}}`,
ptr: new(S6),
out: &S6{embed1{1}},
}, {
// Issue 24153, check that we can still set forwarded fields even in
// the presence of a name conflict.
//
// This relies on obscure behavior of reflect where it is possible
// to set a forwarded exported field on an unexported embedded struct
// even though there is a name conflict, even when it would have been
// impossible to do so according to Go visibility rules.
// Go forbids this because it is ambiguous whether S7.Q refers to
// S7.embed1.Q or S7.embed2.Q. Since embed1 and embed2 are unexported,
// it should be impossible for an external package to set either Q.
//
// It is probably okay for a future reflect change to break this.
CaseName: Name(""),
in: `{"embed1": {"Q": 1}, "Q": 2}`,
ptr: new(S7),
out: &S7{embed1{1}, embed2{2}},
}, {
// Issue 24153, similar to the S7 case.
CaseName: Name(""),
in: `{"embed1": {"Q": 1}, "embed2": {"Q": 2}, "Q": 3}`,
ptr: new(S8),
out: &S8{embed1{1}, embed2{2}, 3},
}, {
// Issue 228145, similar to the cases above.
CaseName: Name(""),
in: `{"embed": {}}`,
ptr: new(S9),
out: &S9{},
Joe Tsai
committed
}}
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
err := Unmarshal([]byte(tt.in), tt.ptr)
if !equalError(err, tt.err) {
t.Errorf("%s: Unmarshal error:\n\tgot: %v\n\twant: %v", tt.Where, err, tt.err)
}
if !reflect.DeepEqual(tt.ptr, tt.out) {
t.Errorf("%s: Unmarshal:\n\tgot: %#+v\n\twant: %#+v", tt.Where, tt.ptr, tt.out)
}
})
Joe Tsai
committed
}
}
func TestUnmarshalErrorAfterMultipleJSON(t *testing.T) {
tests := []struct {
in string
err error
}{{
CaseName: Name(""),
in: `1 false null :`,
err: &SyntaxError{"invalid character ':' looking for beginning of value", 14},
CaseName: Name(""),
in: `1 [] [,]`,
err: &SyntaxError{"invalid character ',' looking for beginning of value", 7},
CaseName: Name(""),
in: `1 [] [true:]`,
err: &SyntaxError{"invalid character ':' after array element", 11},
CaseName: Name(""),
in: `1 {} {"x"=}`,
err: &SyntaxError{"invalid character '=' after object key", 14},
CaseName: Name(""),
in: `falsetruenul#`,
err: &SyntaxError{"invalid character '#' in literal null (expecting 'l')", 13},
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
dec := NewDecoder(strings.NewReader(tt.in))
var err error
for err == nil {
var v any
err = dec.Decode(&v)
if !reflect.DeepEqual(err, tt.err) {
t.Errorf("%s: Decode error:\n\tgot: %v\n\twant: %v", tt.Where, err, tt.err)
}
})
type unmarshalPanic struct{}
func (unmarshalPanic) UnmarshalJSON([]byte) error { panic(0xdead) }
func TestUnmarshalPanic(t *testing.T) {
defer func() {
if got := recover(); !reflect.DeepEqual(got, 0xdead) {
t.Errorf("panic() = (%T)(%v), want 0xdead", got, got)
}
}()
Unmarshal([]byte("{}"), &unmarshalPanic{})
t.Fatalf("Unmarshal should have panicked")
}
// The decoder used to hang if decoding into an interface pointing to its own address.
// See golang.org/issues/31740.
func TestUnmarshalRecursivePointer(t *testing.T) {
v = &v
data := []byte(`{"a": "b"}`)
if err := Unmarshal(data, v); err != nil {
Cuong Manh Le
committed
type textUnmarshalerString string
func (m *textUnmarshalerString) UnmarshalText(text []byte) error {
*m = textUnmarshalerString(strings.ToLower(string(text)))
return nil
}
// Test unmarshal to a map, where the map key is a user defined type.
Cuong Manh Le
committed
// See golang.org/issues/34437.
func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
var p map[textUnmarshalerString]string
if err := Unmarshal([]byte(`{"FOO": "1"}`), &p); err != nil {
Cuong Manh Le
committed
}
if _, ok := p["foo"]; !ok {
}
}
func TestUnmarshalRescanLiteralMangledUnquote(t *testing.T) {
// See golang.org/issues/38105.
var p map[textUnmarshalerString]string
if err := Unmarshal([]byte(`{"开源":"12345开源"}`), &p); err != nil {
}
if _, ok := p["开源"]; !ok {
}
// See golang.org/issues/38126.
type T struct {
F1 string `json:"F1,string"`
}
if err != nil {
var gotT T
if err := Unmarshal(b, &gotT); err != nil {
t.Fatalf("Unmarshal error: %v", err)
if gotT != wantT {
t.Errorf("Marshal/Unmarshal roundtrip:\n\tgot: %q\n\twant: %q", gotT, wantT)
Cuong Manh Le
committed
}
// See golang.org/issues/39555.
input := map[textUnmarshalerString]string{"FOO": "", `"`: ""}
encoded, err := Marshal(input)
if err != nil {
}
var got map[textUnmarshalerString]string
if err := Unmarshal(encoded, &got); err != nil {
}
want := map[textUnmarshalerString]string{"foo": "", `"`: ""}
if !maps.Equal(got, want) {
t.Errorf("Marshal/Unmarshal roundtrip:\n\tgot: %q\n\twant: %q", gotT, wantT)
Cuong Manh Le
committed
}
func TestUnmarshalMaxDepth(t *testing.T) {
data string
errMaxDepth bool
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
}{{
CaseName: Name("ArrayUnderMaxNestingDepth"),
data: `{"a":` + strings.Repeat(`[`, 10000-1) + strings.Repeat(`]`, 10000-1) + `}`,
errMaxDepth: false,
}, {
CaseName: Name("ArrayOverMaxNestingDepth"),
data: `{"a":` + strings.Repeat(`[`, 10000) + strings.Repeat(`]`, 10000) + `}`,
errMaxDepth: true,
}, {
CaseName: Name("ArrayOverStackDepth"),
data: `{"a":` + strings.Repeat(`[`, 3000000) + strings.Repeat(`]`, 3000000) + `}`,
errMaxDepth: true,
}, {
CaseName: Name("ObjectUnderMaxNestingDepth"),
data: `{"a":` + strings.Repeat(`{"a":`, 10000-1) + `0` + strings.Repeat(`}`, 10000-1) + `}`,
errMaxDepth: false,
}, {
CaseName: Name("ObjectOverMaxNestingDepth"),
data: `{"a":` + strings.Repeat(`{"a":`, 10000) + `0` + strings.Repeat(`}`, 10000) + `}`,
errMaxDepth: true,
}, {
CaseName: Name("ObjectOverStackDepth"),
data: `{"a":` + strings.Repeat(`{"a":`, 3000000) + `0` + strings.Repeat(`}`, 3000000) + `}`,
errMaxDepth: true,
}}
}{{
CaseName: Name("unstructured"),
newValue: func() any {
var v any
return &v
}, {
CaseName: Name("typed named field"),
newValue: func() any {
v := struct {
A any `json:"a"`
}{}
return &v
}, {
CaseName: Name("typed missing field"),
newValue: func() any {
v := struct {
B any `json:"b"`
}{}
return &v
}, {
CaseName: Name("custom unmarshaler"),
newValue: func() any {
v := unmarshaler{}
return &v
for _, target := range targets {
t.Run(target.Name+"-"+tt.Name, func(t *testing.T) {
err := Unmarshal([]byte(tt.data), target.newValue())
if !tt.errMaxDepth {
t.Errorf("%s: %s: Unmarshal error: %v", tt.Where, target.Where, err)
if err == nil || !strings.Contains(err.Error(), "exceeded max depth") {
t.Errorf("%s: %s: Unmarshal error:\n\tgot: %v\n\twant: exceeded max depth", tt.Where, target.Where, err)
}
}
})
}
}
}