Skip to content
Snippets Groups Projects
decode_test.go 67.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • // 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 (
    
    type T struct {
    	X string
    	Y int
    
    type U struct {
    	Alphabet string `json:"alpha"`
    }
    
    
    type V struct {
    
    	F2 int32
    	F3 Number
    
    type P struct {
    	PP PP
    }
    
    type PP struct {
    
    type SS string
    
    func (*SS) UnmarshalJSON(data []byte) error {
    
    	return &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[SS]()}
    
    type TAlias T
    
    func (tt *TAlias) UnmarshalJSON(data []byte) error {
    	t := T{}
    	if err := Unmarshal(data, &t); err != nil {
    		return err
    	}
    	*tt = TAlias(t)
    	return nil
    }
    
    type TOuter struct {
    	T TAlias
    }
    
    
    // ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and
    
    // without UseNumber
    
    var ifaceNumAsFloat64 = map[string]any{
    
    	"k1": float64(1),
    	"k2": "s",
    
    	"k3": []any{float64(1), float64(2.0), float64(3e-3)},
    	"k4": map[string]any{"kk1": "s", "kk2": float64(2)},
    
    var ifaceNumAsNumber = map[string]any{
    
    	"k1": Number("1"),
    	"k2": "s",
    
    	"k3": []any{Number("1"), Number("2.0"), Number("3e-3")},
    	"k4": map[string]any{"kk1": "s", "kk2": Number("2")},
    
    type tx struct {
    	x int
    }
    
    
    // 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.
    
    Russ Cox's avatar
    Russ Cox committed
    type ustruct struct {
    	M unmarshaler
    }
    
    
    func (u unmarshalerText) MarshalText() ([]byte, error) {
    	return []byte(u.A + ":" + u.B), nil
    
    }
    
    func (u *unmarshalerText) UnmarshalText(b []byte) error {
    
    	pos := bytes.IndexByte(b, ':')
    
    	if pos == -1 {
    		return errors.New("missing separator")
    	}
    	u.A, u.B = string(b[:pos]), string(b[pos+1:])
    
    	return nil
    }
    
    var _ encoding.TextUnmarshaler = (*unmarshalerText)(nil)
    
    type ustructText struct {
    	M unmarshalerText
    }
    
    
    // u8marshal is an integer type that can marshal/unmarshal itself.
    type u8marshal uint8
    
    func (u8 u8marshal) MarshalText() ([]byte, error) {
    	return []byte(fmt.Sprintf("u%d", u8)), nil
    }
    
    var errMissingU8Prefix = errors.New("missing 'u' prefix")
    
    func (u8 *u8marshal) UnmarshalText(b []byte) error {
    	if !bytes.HasPrefix(b, []byte{'u'}) {
    		return errMissingU8Prefix
    	}
    	n, err := strconv.Atoi(string(b[1:]))
    	if err != nil {
    		return err
    	}
    	*u8 = u8marshal(n)
    	return nil
    }
    
    var _ encoding.TextUnmarshaler = (*u8marshal)(nil)
    
    
    var (
    	umtrue   = unmarshaler{true}
    
    	umslice  = []unmarshaler{{true}}
    
    Russ Cox's avatar
    Russ Cox committed
    	umstruct = ustruct{unmarshaler{true}}
    
    	umtrueXY   = unmarshalerText{"x", "y"}
    	umsliceXY  = []unmarshalerText{{"x", "y"}}
    	umstructXY = ustructText{unmarshalerText{"x", "y"}}
    
    	ummapXY = map[unmarshalerText]bool{{"x", "y"}: true}
    
    // Test data structures for anonymous fields.
    
    type Point struct {
    	Z int
    }
    
    type Top struct {
    	Level0 int
    	Embed0
    	*Embed0a
    	*Embed0b `json:"e,omitempty"` // treated as named
    	Embed0c  `json:"-"`           // ignored
    	Loop
    	Embed0p // has Point with X, Y, used
    	Embed0q // has Point with Z, used
    
    	embed   // contains exported field
    
    }
    
    type Embed0 struct {
    	Level1a int // overridden by Embed0a's Level1a with json tag
    	Level1b int // used because Embed0a's Level1b is renamed
    	Level1c int // used because Embed0a's Level1c is ignored
    	Level1d int // annihilated by Embed0a's Level1d
    	Level1e int `json:"x"` // annihilated by Embed0a.Level1e
    }
    
    type Embed0a struct {
    	Level1a int `json:"Level1a,omitempty"`
    	Level1b int `json:"LEVEL1B,omitempty"`
    	Level1c int `json:"-"`
    	Level1d int // annihilated by Embed0's Level1d
    	Level1f int `json:"x"` // annihilated by Embed0's Level1e
    }
    
    type Embed0b Embed0
    
    type Embed0c Embed0
    
    type Embed0p struct {
    	image.Point
    }
    
    type Embed0q struct {
    	Point
    }
    
    
    type Loop struct {
    	Loop1 int `json:",omitempty"`
    	Loop2 int `json:",omitempty"`
    	*Loop
    }
    
    // From reflect test:
    // The X in S6 and S7 annihilate, but they also block the X in S8.S9.
    type S5 struct {
    	S6
    	S7
    	S8
    }
    
    type S6 struct {
    	X int
    }
    
    type S7 S6
    
    type S8 struct {
    	S9
    }
    
    type S9 struct {
    	X int
    	Y int
    }
    
    // From reflect test:
    // The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9.
    type S10 struct {
    	S11
    	S12
    	S13
    }
    
    type S11 struct {
    	S6
    }
    
    type S12 struct {
    	S6
    }
    
    type S13 struct {
    	S8
    }
    
    type Ambig struct {
    	// Given "hello", the first match should win.
    	First  int `json:"HELLO"`
    	Second int `json:"Hello"`
    }
    
    
    type unexportedWithMethods struct{}
    
    func (unexportedWithMethods) F() {}
    
    
    type byteWithMarshalJSON byte
    
    func (b byteWithMarshalJSON) MarshalJSON() ([]byte, error) {
    	return []byte(fmt.Sprintf(`"Z%.2x"`, byte(b))), nil
    }
    
    func (b *byteWithMarshalJSON) UnmarshalJSON(data []byte) error {
    	if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' {
    		return fmt.Errorf("bad quoted string")
    	}
    	i, err := strconv.ParseInt(string(data[2:4]), 16, 8)
    	if err != nil {
    		return fmt.Errorf("bad hex")
    	}
    	*b = byteWithMarshalJSON(i)
    	return nil
    }
    
    type byteWithPtrMarshalJSON byte
    
    func (b *byteWithPtrMarshalJSON) MarshalJSON() ([]byte, error) {
    	return byteWithMarshalJSON(*b).MarshalJSON()
    }
    
    func (b *byteWithPtrMarshalJSON) UnmarshalJSON(data []byte) error {
    	return (*byteWithMarshalJSON)(b).UnmarshalJSON(data)
    }
    
    type byteWithMarshalText byte
    
    func (b byteWithMarshalText) MarshalText() ([]byte, error) {
    	return []byte(fmt.Sprintf(`Z%.2x`, byte(b))), nil
    }
    
    func (b *byteWithMarshalText) UnmarshalText(data []byte) error {
    	if len(data) != 3 || data[0] != 'Z' {
    		return fmt.Errorf("bad quoted string")
    	}
    	i, err := strconv.ParseInt(string(data[1:3]), 16, 8)
    	if err != nil {
    		return fmt.Errorf("bad hex")
    	}
    	*b = byteWithMarshalText(i)
    	return nil
    }
    
    type byteWithPtrMarshalText byte
    
    func (b *byteWithPtrMarshalText) MarshalText() ([]byte, error) {
    	return byteWithMarshalText(*b).MarshalText()
    }
    
    func (b *byteWithPtrMarshalText) UnmarshalText(data []byte) error {
    	return (*byteWithMarshalText)(b).UnmarshalText(data)
    }
    
    type intWithMarshalJSON int
    
    func (b intWithMarshalJSON) MarshalJSON() ([]byte, error) {
    	return []byte(fmt.Sprintf(`"Z%.2x"`, int(b))), nil
    }
    
    func (b *intWithMarshalJSON) UnmarshalJSON(data []byte) error {
    	if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' {
    		return fmt.Errorf("bad quoted string")
    	}
    	i, err := strconv.ParseInt(string(data[2:4]), 16, 8)
    	if err != nil {
    		return fmt.Errorf("bad hex")
    	}
    	*b = intWithMarshalJSON(i)
    	return nil
    }
    
    type intWithPtrMarshalJSON int
    
    func (b *intWithPtrMarshalJSON) MarshalJSON() ([]byte, error) {
    	return intWithMarshalJSON(*b).MarshalJSON()
    }
    
    func (b *intWithPtrMarshalJSON) UnmarshalJSON(data []byte) error {
    	return (*intWithMarshalJSON)(b).UnmarshalJSON(data)
    }
    
    type intWithMarshalText int
    
    func (b intWithMarshalText) MarshalText() ([]byte, error) {
    	return []byte(fmt.Sprintf(`Z%.2x`, int(b))), nil
    }
    
    func (b *intWithMarshalText) UnmarshalText(data []byte) error {
    	if len(data) != 3 || data[0] != 'Z' {
    		return fmt.Errorf("bad quoted string")
    	}
    	i, err := strconv.ParseInt(string(data[1:3]), 16, 8)
    	if err != nil {
    		return fmt.Errorf("bad hex")
    	}
    	*b = intWithMarshalText(i)
    	return nil
    }
    
    type intWithPtrMarshalText int
    
    func (b *intWithPtrMarshalText) MarshalText() ([]byte, error) {
    	return intWithMarshalText(*b).MarshalText()
    }
    
    func (b *intWithPtrMarshalText) UnmarshalText(data []byte) error {
    	return (*intWithMarshalText)(b).UnmarshalText(data)
    }
    
    
    type mapStringToStringData struct {
    	Data map[string]string `json:"data"`
    }
    
    
    type B struct {
    	B bool `json:",string"`
    }
    
    
    var unmarshalTests = []struct {
    	CaseName
    	in                    string
    	ptr                   any // new(type)
    	out                   any
    	err                   error
    	useNumber             bool
    	golden                bool
    	disallowUnknownFields bool
    }{
    
    	// basic types
    
    	{CaseName: Name(""), in: `true`, ptr: new(bool), out: true},
    	{CaseName: Name(""), in: `1`, ptr: new(int), out: 1},
    	{CaseName: Name(""), in: `1.2`, ptr: new(float64), out: 1.2},
    	{CaseName: Name(""), in: `-5`, ptr: new(int16), out: int16(-5)},
    	{CaseName: Name(""), in: `2`, ptr: new(Number), out: Number("2"), useNumber: true},
    	{CaseName: Name(""), in: `2`, ptr: new(Number), out: Number("2")},
    	{CaseName: Name(""), in: `2`, ptr: new(any), out: float64(2.0)},
    	{CaseName: Name(""), in: `2`, ptr: new(any), out: Number("2"), useNumber: true},
    	{CaseName: Name(""), in: `"a\u1234"`, ptr: new(string), out: "a\u1234"},
    	{CaseName: Name(""), in: `"http:\/\/"`, ptr: new(string), out: "http://"},
    	{CaseName: Name(""), in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},
    	{CaseName: Name(""), in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
    	{CaseName: Name(""), in: "null", ptr: new(any), out: nil},
    	{CaseName: Name(""), in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeFor[string](), 7, "T", "X"}},
    	{CaseName: Name(""), in: `{"X": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 8, "T", "X"}},
    	{CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), out: tx{}},
    	{CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), out: tx{}},
    	{CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), err: fmt.Errorf("json: unknown field \"x\""), disallowUnknownFields: true},
    	{CaseName: Name(""), in: `{"S": 23}`, ptr: new(W), out: W{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[SS](), 0, "W", "S"}},
    
    	{CaseName: Name(""), in: `{"T": {"X": 23}}`, ptr: new(TOuter), out: TOuter{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 0, "TOuter", "T.X"}},
    
    	{CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
    	{CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
    	{CaseName: Name(""), in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsFloat64},
    	{CaseName: Name(""), in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsNumber, useNumber: true},
    
    	// raw values with whitespace
    
    	{CaseName: Name(""), in: "\n true ", ptr: new(bool), out: true},
    	{CaseName: Name(""), in: "\t 1 ", ptr: new(int), out: 1},
    	{CaseName: Name(""), in: "\r 1.2 ", ptr: new(float64), out: 1.2},
    	{CaseName: Name(""), in: "\t -5 \n", ptr: new(int16), out: int16(-5)},
    	{CaseName: Name(""), in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"},
    
    	// Z has a "-" tag.
    
    	{CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}},
    	{CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), err: fmt.Errorf("json: unknown field \"Z\""), disallowUnknownFields: true},
    
    	{CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}},
    	{CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},
    	{CaseName: Name(""), in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}},
    	{CaseName: Name(""), in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}},
    	{CaseName: Name(""), in: `{"alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},
    
    	// syntax errors
    
    	{CaseName: Name(""), in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}},
    	{CaseName: Name(""), in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}},
    	{CaseName: Name(""), in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true},
    	{CaseName: Name(""), in: `[2, 3`, err: &SyntaxError{msg: "unexpected end of JSON input", Offset: 5}},
    	{CaseName: Name(""), in: `{"F3": -}`, ptr: new(V), out: V{F3: Number("-")}, err: &SyntaxError{msg: "invalid character '}' in numeric literal", Offset: 9}},
    
    	{CaseName: Name(""), in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
    	{CaseName: Name(""), in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}},
    	{CaseName: Name(""), in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
    	{CaseName: Name(""), in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}},
    	{CaseName: Name(""), in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
    	{CaseName: Name(""), in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}},
    	{CaseName: Name(""), in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
    	{CaseName: Name(""), in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}},
    
    	{CaseName: Name(""), in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}},
    	{CaseName: Name(""), in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}},
    	{CaseName: Name(""), in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}},
    	{CaseName: Name(""), in: `[1, 2, 3]`, ptr: new(MustNotUnmarshalJSON), err: errors.New("MustNotUnmarshalJSON was used")},
    
    	// empty array to interface test
    
    	{CaseName: Name(""), in: `[]`, ptr: new([]any), out: []any{}},
    	{CaseName: Name(""), in: `null`, ptr: new([]any), out: []any(nil)},
    	{CaseName: Name(""), in: `{"T":[]}`, ptr: new(map[string]any), out: map[string]any{"T": []any{}}},
    	{CaseName: Name(""), in: `{"T":null}`, ptr: new(map[string]any), out: map[string]any{"T": any(nil)}},
    
    	// composite tests
    
    	{CaseName: Name(""), in: allValueIndent, ptr: new(All), out: allValue},
    	{CaseName: Name(""), in: allValueCompact, ptr: new(All), out: allValue},
    	{CaseName: Name(""), in: allValueIndent, ptr: new(*All), out: &allValue},
    	{CaseName: Name(""), in: allValueCompact, ptr: new(*All), out: &allValue},
    	{CaseName: Name(""), in: pallValueIndent, ptr: new(All), out: pallValue},
    	{CaseName: Name(""), in: pallValueCompact, ptr: new(All), out: pallValue},
    	{CaseName: Name(""), in: pallValueIndent, ptr: new(*All), out: &pallValue},
    	{CaseName: Name(""), in: pallValueCompact, ptr: new(*All), out: &pallValue},
    
    
    	// unmarshal interface test
    
    	{CaseName: Name(""), in: `{"T":false}`, ptr: new(unmarshaler), out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called
    	{CaseName: Name(""), in: `{"T":false}`, ptr: new(*unmarshaler), out: &umtrue},
    	{CaseName: Name(""), in: `[{"T":false}]`, ptr: new([]unmarshaler), out: umslice},
    	{CaseName: Name(""), in: `[{"T":false}]`, ptr: new(*[]unmarshaler), out: &umslice},
    	{CaseName: Name(""), in: `{"M":{"T":"x:y"}}`, ptr: new(ustruct), out: umstruct},
    
    	// UnmarshalText interface test
    
    	{CaseName: Name(""), in: `"x:y"`, ptr: new(unmarshalerText), out: umtrueXY},
    	{CaseName: Name(""), in: `"x:y"`, ptr: new(*unmarshalerText), out: &umtrueXY},
    	{CaseName: Name(""), in: `["x:y"]`, ptr: new([]unmarshalerText), out: umsliceXY},
    	{CaseName: Name(""), in: `["x:y"]`, ptr: new(*[]unmarshalerText), out: &umsliceXY},
    	{CaseName: Name(""), in: `{"M":"x:y"}`, ptr: new(ustructText), out: umstructXY},
    
    	// integer-keyed map test
    	{
    
    		CaseName: Name(""),
    		in:       `{"-1":"a","0":"b","1":"c"}`,
    		ptr:      new(map[int]string),
    		out:      map[int]string{-1: "a", 0: "b", 1: "c"},
    
    		CaseName: Name(""),
    		in:       `{"0":"a","10":"c","9":"b"}`,
    		ptr:      new(map[u8]string),
    		out:      map[u8]string{0: "a", 9: "b", 10: "c"},
    
    		CaseName: Name(""),
    		in:       `{"-9223372036854775808":"min","9223372036854775807":"max"}`,
    		ptr:      new(map[int64]string),
    		out:      map[int64]string{math.MinInt64: "min", math.MaxInt64: "max"},
    
    		CaseName: Name(""),
    		in:       `{"18446744073709551615":"max"}`,
    		ptr:      new(map[uint64]string),
    		out:      map[uint64]string{math.MaxUint64: "max"},
    
    		CaseName: Name(""),
    		in:       `{"0":false,"10":true}`,
    		ptr:      new(map[uintptr]bool),
    		out:      map[uintptr]bool{0: false, 10: true},
    
    	},
    
    	// Check that MarshalText and UnmarshalText take precedence
    	// over default integer handling in map keys.
    	{
    
    		CaseName: Name(""),
    		in:       `{"u2":4}`,
    		ptr:      new(map[u8marshal]int),
    		out:      map[u8marshal]int{2: 4},
    
    		CaseName: Name(""),
    		in:       `{"2":4}`,
    		ptr:      new(map[u8marshal]int),
    		err:      errMissingU8Prefix,
    
    	},
    
    	// integer-keyed map errors
    	{
    
    		CaseName: Name(""),
    		in:       `{"abc":"abc"}`,
    		ptr:      new(map[int]string),
    		err:      &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeFor[int](), Offset: 2},
    
    		CaseName: Name(""),
    		in:       `{"256":"abc"}`,
    		ptr:      new(map[uint8]string),
    		err:      &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeFor[uint8](), Offset: 2},
    
    		CaseName: Name(""),
    		in:       `{"128":"abc"}`,
    		ptr:      new(map[int8]string),
    		err:      &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeFor[int8](), Offset: 2},
    
    		CaseName: Name(""),
    		in:       `{"-1":"abc"}`,
    		ptr:      new(map[uint8]string),
    		err:      &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeFor[uint8](), Offset: 2},
    
    		CaseName: Name(""),
    		in:       `{"F":{"a":2,"3":4}}`,
    		ptr:      new(map[string]map[int]int),
    		err:      &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[int](), Offset: 7},
    
    		CaseName: Name(""),
    		in:       `{"F":{"a":2,"3":4}}`,
    		ptr:      new(map[string]map[uint]int),
    		err:      &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[uint](), Offset: 7},
    
    
    	// Map keys can be encoding.TextUnmarshalers.
    
    	{CaseName: Name(""), in: `{"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},
    
    	// If multiple values for the same key exists, only the most recent value is used.
    
    	{CaseName: Name(""), in: `{"x:y":false,"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},
    
    		CaseName: Name(""),
    
    		in: `{
    			"Level0": 1,
    			"Level1b": 2,
    			"Level1c": 3,
    			"x": 4,
    			"Level1a": 5,
    			"LEVEL1B": 6,
    			"e": {
    				"Level1a": 8,
    				"Level1b": 9,
    				"Level1c": 10,
    				"Level1d": 11,
    				"x": 12
    			},
    			"Loop1": 13,
    			"Loop2": 14,
    			"X": 15,
    			"Y": 16,
    
    		}`,
    		ptr: new(Top),
    		out: Top{
    			Level0: 1,
    			Embed0: Embed0{
    				Level1b: 2,
    				Level1c: 3,
    			},
    			Embed0a: &Embed0a{
    				Level1a: 5,
    				Level1b: 6,
    			},
    			Embed0b: &Embed0b{
    				Level1a: 8,
    				Level1b: 9,
    				Level1c: 10,
    				Level1d: 11,
    				Level1e: 12,
    			},
    			Loop: Loop{
    				Loop1: 13,
    				Loop2: 14,
    			},
    			Embed0p: Embed0p{
    				Point: image.Point{X: 15, Y: 16},
    			},
    			Embed0q: Embed0q{
    				Point: Point{Z: 17},
    			},
    
    		CaseName: Name(""),
    		in:       `{"hello": 1}`,
    		ptr:      new(Ambig),
    		out:      Ambig{First: 1},
    
    		CaseName: Name(""),
    		in:       `{"X": 1,"Y":2}`,
    		ptr:      new(S5),
    		out:      S5{S8: S8{S9: S9{Y: 2}}},
    
    		CaseName:              Name(""),
    
    		in:                    `{"X": 1,"Y":2}`,
    		ptr:                   new(S5),
    		err:                   fmt.Errorf("json: unknown field \"X\""),
    
    		disallowUnknownFields: true,
    	},
    
    		CaseName: Name(""),
    		in:       `{"X": 1,"Y":2}`,
    		ptr:      new(S10),
    		out:      S10{S13: S13{S8: S8{S9: S9{Y: 2}}}},
    
    		CaseName:              Name(""),
    
    		in:                    `{"X": 1,"Y":2}`,
    		ptr:                   new(S10),
    		err:                   fmt.Errorf("json: unknown field \"X\""),
    
    		disallowUnknownFields: true,
    	},
    
    		CaseName: Name(""),
    		in:       `{"I": 0, "I": null, "J": null}`,
    		ptr:      new(DoublePtr),
    		out:      DoublePtr{I: nil, J: nil},
    
    
    	// invalid UTF-8 is coerced to valid UTF-8.
    	{
    
    		CaseName: Name(""),
    		in:       "\"hello\xffworld\"",
    		ptr:      new(string),
    		out:      "hello\ufffdworld",
    
    		CaseName: Name(""),
    		in:       "\"hello\xc2\xc2world\"",
    		ptr:      new(string),
    		out:      "hello\ufffd\ufffdworld",
    
    		CaseName: Name(""),
    		in:       "\"hello\xc2\xffworld\"",
    		ptr:      new(string),
    		out:      "hello\ufffd\ufffdworld",
    
    		CaseName: Name(""),
    		in:       "\"hello\\ud800world\"",
    		ptr:      new(string),
    		out:      "hello\ufffdworld",
    
    		CaseName: Name(""),
    		in:       "\"hello\\ud800\\ud800world\"",
    		ptr:      new(string),
    		out:      "hello\ufffd\ufffdworld",
    
    		CaseName: Name(""),
    		in:       "\"hello\\ud800\\ud800world\"",
    		ptr:      new(string),
    		out:      "hello\ufffd\ufffdworld",
    
    		CaseName: Name(""),
    		in:       "\"hello\xed\xa0\x80\xed\xb0\x80world\"",
    		ptr:      new(string),
    		out:      "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld",
    
    	// Used to be issue 8305, but time.Time implements encoding.TextUnmarshaler so this works now.
    
    		CaseName: Name(""),
    		in:       `{"2009-11-10T23:00:00Z": "hello world"}`,
    		ptr:      new(map[time.Time]string),
    		out:      map[time.Time]string{time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC): "hello world"},
    
    		CaseName: Name(""),
    		in:       `{"2009-11-10T23:00:00Z": "hello world"}`,
    		ptr:      new(map[Point]string),
    		err:      &UnmarshalTypeError{Value: "object", Type: reflect.TypeFor[map[Point]string](), Offset: 1},
    
    		CaseName: Name(""),
    		in:       `{"asdf": "hello world"}`,
    		ptr:      new(map[unmarshaler]string),
    		err:      &UnmarshalTypeError{Value: "object", Type: reflect.TypeFor[map[unmarshaler]string](), Offset: 1},
    
    
    	// related to issue 13783.
    	// Go 1.7 changed marshaling a slice of typed byte to use the methods on the byte type,
    	// similar to marshaling a slice of typed int.
    	// These tests check that, assuming the byte type also has valid decoding methods,
    	// either the old base64 string encoding or the new per-element encoding can be
    	// successfully unmarshaled. The custom unmarshalers were accessible in earlier
    	// versions of Go, even though the custom marshaler was not.
    	{
    
    		CaseName: Name(""),
    		in:       `"AQID"`,
    		ptr:      new([]byteWithMarshalJSON),
    		out:      []byteWithMarshalJSON{1, 2, 3},
    
    		CaseName: Name(""),
    		in:       `["Z01","Z02","Z03"]`,
    		ptr:      new([]byteWithMarshalJSON),
    		out:      []byteWithMarshalJSON{1, 2, 3},
    		golden:   true,
    
    		CaseName: Name(""),
    		in:       `"AQID"`,
    		ptr:      new([]byteWithMarshalText),
    		out:      []byteWithMarshalText{1, 2, 3},
    
    		CaseName: Name(""),
    		in:       `["Z01","Z02","Z03"]`,
    		ptr:      new([]byteWithMarshalText),
    		out:      []byteWithMarshalText{1, 2, 3},
    		golden:   true,
    
    		CaseName: Name(""),
    		in:       `"AQID"`,
    		ptr:      new([]byteWithPtrMarshalJSON),
    		out:      []byteWithPtrMarshalJSON{1, 2, 3},
    
    		CaseName: Name(""),
    		in:       `["Z01","Z02","Z03"]`,
    		ptr:      new([]byteWithPtrMarshalJSON),
    		out:      []byteWithPtrMarshalJSON{1, 2, 3},
    		golden:   true,
    
    		CaseName: Name(""),
    		in:       `"AQID"`,
    		ptr:      new([]byteWithPtrMarshalText),
    		out:      []byteWithPtrMarshalText{1, 2, 3},
    
    		CaseName: Name(""),
    		in:       `["Z01","Z02","Z03"]`,
    		ptr:      new([]byteWithPtrMarshalText),
    		out:      []byteWithPtrMarshalText{1, 2, 3},
    		golden:   true,
    
    	},
    
    	// ints work with the marshaler but not the base64 []byte case
    	{
    
    		CaseName: Name(""),
    		in:       `["Z01","Z02","Z03"]`,
    		ptr:      new([]intWithMarshalJSON),
    		out:      []intWithMarshalJSON{1, 2, 3},
    		golden:   true,
    
    		CaseName: Name(""),
    		in:       `["Z01","Z02","Z03"]`,
    		ptr:      new([]intWithMarshalText),
    		out:      []intWithMarshalText{1, 2, 3},
    		golden:   true,
    
    		CaseName: Name(""),
    		in:       `["Z01","Z02","Z03"]`,
    		ptr:      new([]intWithPtrMarshalJSON),
    		out:      []intWithPtrMarshalJSON{1, 2, 3},
    		golden:   true,
    
    		CaseName: Name(""),
    		in:       `["Z01","Z02","Z03"]`,
    		ptr:      new([]intWithPtrMarshalText),
    		out:      []intWithPtrMarshalText{1, 2, 3},
    		golden:   true,
    	},
    
    	{CaseName: Name(""), in: `0.000001`, ptr: new(float64), out: 0.000001, golden: true},
    	{CaseName: Name(""), in: `1e-7`, ptr: new(float64), out: 1e-7, golden: true},
    	{CaseName: Name(""), in: `100000000000000000000`, ptr: new(float64), out: 100000000000000000000.0, golden: true},
    	{CaseName: Name(""), in: `1e+21`, ptr: new(float64), out: 1e21, golden: true},
    	{CaseName: Name(""), in: `-0.000001`, ptr: new(float64), out: -0.000001, golden: true},
    	{CaseName: Name(""), in: `-1e-7`, ptr: new(float64), out: -1e-7, golden: true},
    	{CaseName: Name(""), in: `-100000000000000000000`, ptr: new(float64), out: -100000000000000000000.0, golden: true},
    	{CaseName: Name(""), in: `-1e+21`, ptr: new(float64), out: -1e21, golden: true},
    	{CaseName: Name(""), in: `999999999999999900000`, ptr: new(float64), out: 999999999999999900000.0, golden: true},
    	{CaseName: Name(""), in: `9007199254740992`, ptr: new(float64), out: 9007199254740992.0, golden: true},
    	{CaseName: Name(""), in: `9007199254740993`, ptr: new(float64), out: 9007199254740992.0, golden: false},
    
    		CaseName: Name(""),
    		in:       `{"V": {"F2": "hello"}}`,
    		ptr:      new(VOuter),
    
    		err: &UnmarshalTypeError{
    			Value:  "string",
    			Struct: "V",
    
    			Type:   reflect.TypeFor[int32](),
    
    		CaseName: Name(""),
    		in:       `{"V": {"F4": {}, "F2": "hello"}}`,
    		ptr:      new(VOuter),
    
    		err: &UnmarshalTypeError{
    			Value:  "string",
    			Struct: "V",
    
    			Type:   reflect.TypeFor[int32](),
    
    
    	// issue 15146.
    	// invalid inputs in wrongStringTests below.
    
    	{CaseName: Name(""), in: `{"B":"true"}`, ptr: new(B), out: B{true}, golden: true},
    	{CaseName: Name(""), in: `{"B":"false"}`, ptr: new(B), out: B{false}, golden: true},
    	{CaseName: Name(""), in: `{"B": "maybe"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "maybe" into bool`)},
    	{CaseName: Name(""), in: `{"B": "tru"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "tru" into bool`)},
    	{CaseName: Name(""), in: `{"B": "False"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "False" into bool`)},
    	{CaseName: Name(""), in: `{"B": "null"}`, ptr: new(B), out: B{false}},
    	{CaseName: Name(""), in: `{"B": "nul"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "nul" into bool`)},
    	{CaseName: Name(""), in: `{"B": [2, 3]}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal unquoted value into bool`)},
    
    
    	// additional tests for disallowUnknownFields
    	{
    
    		CaseName: Name(""),
    
    		in: `{
    			"Level0": 1,
    			"Level1b": 2,
    			"Level1c": 3,
    			"x": 4,
    			"Level1a": 5,
    			"LEVEL1B": 6,
    			"e": {
    				"Level1a": 8,
    				"Level1b": 9,
    				"Level1c": 10,
    				"Level1d": 11,
    				"x": 12
    			},
    			"Loop1": 13,
    			"Loop2": 14,
    			"X": 15,
    			"Y": 16,
    			"Z": 17,
    			"Q": 18,
    			"extra": true
    		}`,
    
    		ptr:                   new(Top),
    		err:                   fmt.Errorf("json: unknown field \"extra\""),
    
    		disallowUnknownFields: true,
    	},
    	{
    
    		CaseName: Name(""),
    
    		in: `{
    			"Level0": 1,
    			"Level1b": 2,
    			"Level1c": 3,
    			"x": 4,
    			"Level1a": 5,
    			"LEVEL1B": 6,
    			"e": {
    				"Level1a": 8,
    				"Level1b": 9,
    				"Level1c": 10,
    				"Level1d": 11,
    				"x": 12,
    				"extra": null
    			},
    			"Loop1": 13,
    			"Loop2": 14,
    			"X": 15,
    			"Y": 16,
    			"Z": 17,
    			"Q": 18
    		}`,
    
    		ptr:                   new(Top),
    		err:                   fmt.Errorf("json: unknown field \"extra\""),
    
    		disallowUnknownFields: true,
    	},
    
    	// issue 26444
    	// UnmarshalTypeError without field & struct values
    	{
    
    		CaseName: Name(""),
    		in:       `{"data":{"test1": "bob", "test2": 123}}`,
    		ptr:      new(mapStringToStringData),
    		err:      &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 37, Struct: "mapStringToStringData", Field: "data"},
    
    		CaseName: Name(""),
    		in:       `{"data":{"test1": 123, "test2": "bob"}}`,
    		ptr:      new(mapStringToStringData),
    		err:      &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 21, Struct: "mapStringToStringData", Field: "data"},
    
    
    	// trying to decode JSON arrays or objects via TextUnmarshaler
    	{
    
    		CaseName: Name(""),
    		in:       `[1, 2, 3]`,
    		ptr:      new(MustNotUnmarshalText),
    		err:      &UnmarshalTypeError{Value: "array", Type: reflect.TypeFor[*MustNotUnmarshalText](), Offset: 1},
    
    		CaseName: Name(""),
    		in:       `{"foo": "bar"}`,
    		ptr:      new(MustNotUnmarshalText),
    		err:      &UnmarshalTypeError{Value: "object", Type: reflect.TypeFor[*MustNotUnmarshalText](), Offset: 1},
    
    		CaseName: Name(""),
    		in:       `{"PP": {"T": {"Y": "bad-type"}}}`,