Skip to content
Snippets Groups Projects
decode_test.go 67.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • // 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)
    
    		t.Fatalf("Marshal error: %v", err)
    
    	var got byteKind
    	err = Unmarshal(data, &got)
    
    		t.Fatalf("Unmarshal error: %v", err)
    
    	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)
    
    		t.Fatalf("Marshal error: %v", err)
    
    	var got []Uint8
    	err = Unmarshal(data, &got)
    
    		t.Fatalf("Unmarshal error: %v", err)
    
    	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) {
    
    	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"`
    
    }
    
    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 {
    
    		t.Errorf("Unmarshal error: %v", err)
    
    	}
    	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 := `[{}]`
    
    	var dest [0]any
    
    
    	err := Unmarshal([]byte(json), &dest)
    	if err != nil {
    
    		t.Errorf("Unmarshal error: %v", err)
    
    // 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.
    
    	tests := []struct {
    		CaseName
    
    	}{{
    		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)
    			}
    		})
    
    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)
    			}
    		})
    
    // 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 any               `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 error: %v", err)
    
    	}
    
    	err = Unmarshal(data, &item)
    	if err != nil {
    
    		t.Fatalf("Unmarshal error: %v", err)
    
    // 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) {
    
    	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"`
    		}
    
    		CaseName
    
    		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"),
    
    	}, {
    		// The top level Q field takes precedence.
    
    		CaseName: Name(""),
    		in:       `{"Q":1}`,
    		ptr:      new(S2),
    		out:      &S2{Q: 1},
    
    		CaseName: Name(""),
    		in:       `{"R":2,"Q":1}`,
    		ptr:      new(S3),
    		out:      &S3{embed1: embed1{Q: 1}, R: 2},
    
    	}, {
    		// 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),
    
    	}, {
    		// 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{},
    
    	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)
    			}
    		})
    
    func TestUnmarshalErrorAfterMultipleJSON(t *testing.T) {
    	tests := []struct {
    
    		CaseName
    
    		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 {
    
    		t.Fatalf("Unmarshal error: %v", err)
    
    
    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.
    
    // See golang.org/issues/34437.
    func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
    	var p map[textUnmarshalerString]string
    	if err := Unmarshal([]byte(`{"FOO": "1"}`), &p); err != nil {
    
    		t.Fatalf("Unmarshal error: %v", err)
    
    		t.Errorf(`key "foo" missing in map: %v`, p)
    
    	}
    }
    
    func TestUnmarshalRescanLiteralMangledUnquote(t *testing.T) {
    	// See golang.org/issues/38105.
    	var p map[textUnmarshalerString]string
    	if err := Unmarshal([]byte(`{"开源":"12345开源"}`), &p); err != nil {
    
    		t.Fatalf("Unmarshal error: %v", err)
    
    		t.Errorf(`key "开源" missing in map: %v`, p)
    
    	}
    
    	// See golang.org/issues/38126.
    	type T struct {
    		F1 string `json:"F1,string"`
    	}
    
    	wantT := T{"aaa\tbbb"}
    
    	b, err := Marshal(wantT)
    
    		t.Fatalf("Marshal error: %v", err)
    
    	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)
    
    
    	// See golang.org/issues/39555.
    	input := map[textUnmarshalerString]string{"FOO": "", `"`: ""}
    
    	encoded, err := Marshal(input)
    	if err != nil {
    
    		t.Fatalf("Marshal error: %v", err)
    
    	}
    	var got map[textUnmarshalerString]string
    	if err := Unmarshal(encoded, &got); err != nil {
    
    		t.Fatalf("Unmarshal error: %v", err)
    
    	}
    	want := map[textUnmarshalerString]string{"foo": "", `"`: ""}
    
    	if !maps.Equal(got, want) {
    
    		t.Errorf("Marshal/Unmarshal roundtrip:\n\tgot:  %q\n\twant: %q", gotT, wantT)
    
    
    func TestUnmarshalMaxDepth(t *testing.T) {
    
    	tests := []struct {
    		CaseName
    
    		data        string
    		errMaxDepth bool
    
    	}{{
    		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,
    	}}
    
    
    	targets := []struct {
    
    		CaseName
    
    		newValue func() any
    
    	}{{
    		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 _, tt := range tests {
    
    		for _, target := range targets {
    
    			t.Run(target.Name+"-"+tt.Name, func(t *testing.T) {
    				err := Unmarshal([]byte(tt.data), target.newValue())
    				if !tt.errMaxDepth {
    
    					if err != nil {
    
    						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)