Skip to content
Snippets Groups Projects
decode_test.go 43.3 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 {
    	F1 interface{}
    	F2 int32
    	F3 Number
    
    // ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and
    
    // without UseNumber
    var ifaceNumAsFloat64 = map[string]interface{}{
    	"k1": float64(1),
    	"k2": "s",
    	"k3": []interface{}{float64(1), float64(2.0), float64(3e-3)},
    	"k4": map[string]interface{}{"kk1": "s", "kk2": float64(2)},
    }
    
    var ifaceNumAsNumber = map[string]interface{}{
    	"k1": Number("1"),
    	"k2": "s",
    	"k3": []interface{}{Number("1"), Number("2.0"), Number("3e-3")},
    	"k4": map[string]interface{}{"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.Index(b, []byte(":"))
    	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 (
    	um0, um1 unmarshaler // target2 of unmarshaling
    	ump      = &um1
    	umtrue   = unmarshaler{true}
    
    	umslice  = []unmarshaler{{true}}
    
    Russ Cox's avatar
    Russ Cox committed
    	umslicep = new([]unmarshaler)
    	umstruct = ustruct{unmarshaler{true}}
    
    	um0T, um1T   unmarshalerText // target2 of unmarshaling
    	umpType      = &um1T
    	umtrueXY     = unmarshalerText{"x", "y"}
    	umsliceXY    = []unmarshalerText{{"x", "y"}}
    	umslicepType = new([]unmarshalerText)
    	umstructType = new(ustructText)
    	umstructXY   = ustructText{unmarshalerText{"x", "y"}}
    
    	ummapType = map[unmarshalerText]bool{}
    	ummapXY   = map[unmarshalerText]bool{unmarshalerText{"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 XYZ struct {
    	X interface{}
    	Y interface{}
    	Z interface{}
    }
    
    
    func sliceAddr(x []int) *[]int                 { return &x }
    func mapAddr(x map[string]int) *map[string]int { return &x }
    
    
    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 unmarshalTest struct {
    	in        string
    	ptr       interface{}
    	out       interface{}
    	err       error
    	useNumber bool
    	golden    bool
    }
    
    
    type B struct {
    	B bool `json:",string"`
    }
    
    
    var unmarshalTests = []unmarshalTest{
    	// basic types
    
    	{in: `true`, ptr: new(bool), out: true},
    	{in: `1`, ptr: new(int), out: 1},
    	{in: `1.2`, ptr: new(float64), out: 1.2},
    	{in: `-5`, ptr: new(int16), out: int16(-5)},
    
    	{in: `2`, ptr: new(Number), out: Number("2"), useNumber: true},
    	{in: `2`, ptr: new(Number), out: Number("2")},
    	{in: `2`, ptr: new(interface{}), out: float64(2.0)},
    	{in: `2`, ptr: new(interface{}), out: Number("2"), useNumber: true},
    
    	{in: `"a\u1234"`, ptr: new(string), out: "a\u1234"},
    	{in: `"http:\/\/"`, ptr: new(string), out: "http://"},
    	{in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},
    	{in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
    	{in: "null", ptr: new(interface{}), out: nil},
    
    	{in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7, "T", "X"}},
    
    	{in: `{"x": 1}`, ptr: new(tx), out: tx{}},
    
    	{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
    	{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
    	{in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64},
    	{in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsNumber, useNumber: true},
    
    	// raw values with whitespace
    	{in: "\n true ", ptr: new(bool), out: true},
    	{in: "\t 1 ", ptr: new(int), out: 1},
    	{in: "\r 1.2 ", ptr: new(float64), out: 1.2},
    	{in: "\t -5 \n", ptr: new(int16), out: int16(-5)},
    	{in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"},
    
    
    	// Z has a "-" tag.
    
    	{in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}},
    
    	{in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}},
    	{in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}},
    	{in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}},
    
    	// syntax errors
    
    	{in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}},
    	{in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}},
    
    	{in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true},
    
    	// raw value errors
    	{in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
    	{in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}},
    	{in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
    	{in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}},
    	{in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
    	{in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}},
    	{in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
    	{in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}},
    
    
    	{in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}},
    	{in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}},
    	{in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}},
    
    	// empty array to interface test
    	{in: `[]`, ptr: new([]interface{}), out: []interface{}{}},
    	{in: `null`, ptr: new([]interface{}), out: []interface{}(nil)},
    	{in: `{"T":[]}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": []interface{}{}}},
    	{in: `{"T":null}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": interface{}(nil)}},
    
    
    	// composite tests
    
    	{in: allValueIndent, ptr: new(All), out: allValue},
    	{in: allValueCompact, ptr: new(All), out: allValue},
    	{in: allValueIndent, ptr: new(*All), out: &allValue},
    	{in: allValueCompact, ptr: new(*All), out: &allValue},
    	{in: pallValueIndent, ptr: new(All), out: pallValue},
    	{in: pallValueCompact, ptr: new(All), out: pallValue},
    	{in: pallValueIndent, ptr: new(*All), out: &pallValue},
    	{in: pallValueCompact, ptr: new(*All), out: &pallValue},
    
    
    	// unmarshal interface test
    
    	{in: `{"T":false}`, ptr: &um0, out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called
    	{in: `{"T":false}`, ptr: &ump, out: &umtrue},
    	{in: `[{"T":false}]`, ptr: &umslice, out: umslice},
    	{in: `[{"T":false}]`, ptr: &umslicep, out: &umslice},
    
    	{in: `{"M":{"T":"x:y"}}`, ptr: &umstruct, out: umstruct},
    
    	// UnmarshalText interface test
    
    	{in: `"x:y"`, ptr: &um0T, out: umtrueXY},
    	{in: `"x:y"`, ptr: &umpType, out: &umtrueXY},
    	{in: `["x:y"]`, ptr: &umsliceXY, out: umsliceXY},
    	{in: `["x:y"]`, ptr: &umslicepType, out: &umsliceXY},
    	{in: `{"M":"x:y"}`, ptr: umstructType, out: umstructXY},
    
    
    	// integer-keyed map test
    	{
    		in:  `{"-1":"a","0":"b","1":"c"}`,
    		ptr: new(map[int]string),
    		out: map[int]string{-1: "a", 0: "b", 1: "c"},
    	},
    	{
    		in:  `{"0":"a","10":"c","9":"b"}`,
    		ptr: new(map[u8]string),
    		out: map[u8]string{0: "a", 9: "b", 10: "c"},
    	},
    	{
    		in:  `{"-9223372036854775808":"min","9223372036854775807":"max"}`,
    		ptr: new(map[int64]string),
    		out: map[int64]string{math.MinInt64: "min", math.MaxInt64: "max"},
    	},
    	{
    		in:  `{"18446744073709551615":"max"}`,
    		ptr: new(map[uint64]string),
    		out: map[uint64]string{math.MaxUint64: "max"},
    	},
    	{
    		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.
    	{
    		in:  `{"u2":4}`,
    		ptr: new(map[u8marshal]int),
    		out: map[u8marshal]int{2: 4},
    	},
    	{
    		in:  `{"2":4}`,
    		ptr: new(map[u8marshal]int),
    		err: errMissingU8Prefix,
    	},
    
    	// integer-keyed map errors
    	{
    		in:  `{"abc":"abc"}`,
    		ptr: new(map[int]string),
    
    		err: &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeOf(0), Offset: 2},
    
    	},
    	{
    		in:  `{"256":"abc"}`,
    		ptr: new(map[uint8]string),
    
    		err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeOf(uint8(0)), Offset: 2},
    
    	},
    	{
    		in:  `{"128":"abc"}`,
    		ptr: new(map[int8]string),
    
    		err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeOf(int8(0)), Offset: 2},
    
    	},
    	{
    		in:  `{"-1":"abc"}`,
    		ptr: new(map[uint8]string),
    
    		err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeOf(uint8(0)), Offset: 2},
    
    	},
    
    	// Map keys can be encoding.TextUnmarshalers.
    
    	{in: `{"x:y":true}`, ptr: &ummapType, out: ummapXY},
    	// If multiple values for the same key exists, only the most recent value is used.
    	{in: `{"x:y":false,"x:y":true}`, ptr: &ummapType, out: ummapXY},
    
    	// Overwriting of data.
    	// This is different from package xml, but it's what we've always done.
    	// Now documented and tested.
    	{in: `[2]`, ptr: sliceAddr([]int{1}), out: []int{2}},
    	{in: `{"key": 2}`, ptr: mapAddr(map[string]int{"old": 0, "key": 1}), out: map[string]int{"key": 2}},
    
    
    	{
    		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},
    			},
    
    		},
    	},
    	{
    		in:  `{"hello": 1}`,
    		ptr: new(Ambig),
    		out: Ambig{First: 1},
    	},
    
    	{
    		in:  `{"X": 1,"Y":2}`,
    		ptr: new(S5),
    		out: S5{S8: S8{S9: S9{Y: 2}}},
    	},
    	{
    		in:  `{"X": 1,"Y":2}`,
    		ptr: new(S10),
    		out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}},
    	},
    
    
    	// invalid UTF-8 is coerced to valid UTF-8.
    	{
    		in:  "\"hello\xffworld\"",
    		ptr: new(string),
    		out: "hello\ufffdworld",
    	},
    	{
    		in:  "\"hello\xc2\xc2world\"",
    		ptr: new(string),
    		out: "hello\ufffd\ufffdworld",
    	},
    	{
    		in:  "\"hello\xc2\xffworld\"",
    		ptr: new(string),
    		out: "hello\ufffd\ufffdworld",
    	},
    	{
    		in:  "\"hello\\ud800world\"",
    		ptr: new(string),
    		out: "hello\ufffdworld",
    	},
    	{
    		in:  "\"hello\\ud800\\ud800world\"",
    		ptr: new(string),
    		out: "hello\ufffd\ufffdworld",
    	},
    	{
    		in:  "\"hello\\ud800\\ud800world\"",
    		ptr: new(string),
    		out: "hello\ufffd\ufffdworld",
    	},
    	{
    		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.
    
    	{
    		in:  `{"2009-11-10T23:00:00Z": "hello world"}`,
    		ptr: &map[time.Time]string{},
    
    		out: map[time.Time]string{time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC): "hello world"},
    	},
    
    	// issue 8305
    	{
    		in:  `{"2009-11-10T23:00:00Z": "hello world"}`,
    		ptr: &map[Point]string{},
    
    		err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[Point]string{}), Offset: 1},
    
    	},
    	{
    		in:  `{"asdf": "hello world"}`,
    		ptr: &map[unmarshaler]string{},
    
    		err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(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.
    	{
    		in:  `"AQID"`,
    		ptr: new([]byteWithMarshalJSON),
    		out: []byteWithMarshalJSON{1, 2, 3},
    	},
    	{
    		in:     `["Z01","Z02","Z03"]`,
    		ptr:    new([]byteWithMarshalJSON),
    		out:    []byteWithMarshalJSON{1, 2, 3},
    		golden: true,
    	},
    	{
    		in:  `"AQID"`,
    		ptr: new([]byteWithMarshalText),
    		out: []byteWithMarshalText{1, 2, 3},
    	},
    	{
    		in:     `["Z01","Z02","Z03"]`,
    		ptr:    new([]byteWithMarshalText),
    		out:    []byteWithMarshalText{1, 2, 3},
    		golden: true,
    	},
    	{
    		in:  `"AQID"`,
    		ptr: new([]byteWithPtrMarshalJSON),
    		out: []byteWithPtrMarshalJSON{1, 2, 3},
    	},
    	{
    		in:     `["Z01","Z02","Z03"]`,
    		ptr:    new([]byteWithPtrMarshalJSON),
    		out:    []byteWithPtrMarshalJSON{1, 2, 3},
    		golden: true,
    	},
    	{
    		in:  `"AQID"`,
    		ptr: new([]byteWithPtrMarshalText),
    		out: []byteWithPtrMarshalText{1, 2, 3},
    	},
    	{
    		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
    	{
    		in:     `["Z01","Z02","Z03"]`,
    		ptr:    new([]intWithMarshalJSON),
    		out:    []intWithMarshalJSON{1, 2, 3},
    		golden: true,
    	},
    	{
    		in:     `["Z01","Z02","Z03"]`,
    		ptr:    new([]intWithMarshalText),
    		out:    []intWithMarshalText{1, 2, 3},
    		golden: true,
    	},
    	{
    		in:     `["Z01","Z02","Z03"]`,
    		ptr:    new([]intWithPtrMarshalJSON),
    		out:    []intWithPtrMarshalJSON{1, 2, 3},
    		golden: true,
    	},
    	{
    		in:     `["Z01","Z02","Z03"]`,
    		ptr:    new([]intWithPtrMarshalText),
    		out:    []intWithPtrMarshalText{1, 2, 3},
    		golden: true,
    	},
    
    
    	{in: `0.000001`, ptr: new(float64), out: 0.000001, golden: true},
    	{in: `1e-7`, ptr: new(float64), out: 1e-7, golden: true},
    	{in: `100000000000000000000`, ptr: new(float64), out: 100000000000000000000.0, golden: true},
    	{in: `1e+21`, ptr: new(float64), out: 1e21, golden: true},
    	{in: `-0.000001`, ptr: new(float64), out: -0.000001, golden: true},
    	{in: `-1e-7`, ptr: new(float64), out: -1e-7, golden: true},
    	{in: `-100000000000000000000`, ptr: new(float64), out: -100000000000000000000.0, golden: true},
    	{in: `-1e+21`, ptr: new(float64), out: -1e21, golden: true},
    	{in: `999999999999999900000`, ptr: new(float64), out: 999999999999999900000.0, golden: true},
    	{in: `9007199254740992`, ptr: new(float64), out: 9007199254740992.0, golden: true},
    	{in: `9007199254740993`, ptr: new(float64), out: 9007199254740992.0, golden: false},
    
    
    	{
    		in:  `{"V": {"F2": "hello"}}`,
    		ptr: new(VOuter),
    		err: &UnmarshalTypeError{
    			Value:  "string",
    			Struct: "V",
    			Field:  "F2",
    			Type:   reflect.TypeOf(int32(0)),
    			Offset: 20,
    		},
    	},
    	{
    		in:  `{"V": {"F4": {}, "F2": "hello"}}`,
    		ptr: new(VOuter),
    		err: &UnmarshalTypeError{
    			Value:  "string",
    			Struct: "V",
    			Field:  "F2",
    			Type:   reflect.TypeOf(int32(0)),
    			Offset: 30,
    		},
    	},
    
    
    	// issue 15146.
    	// invalid inputs in wrongStringTests below.
    	{in: `{"B":"true"}`, ptr: new(B), out: B{true}, golden: true},
    	{in: `{"B":"false"}`, ptr: new(B), out: B{false}, golden: true},
    	{in: `{"B": "maybe"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "maybe" into bool`)},
    	{in: `{"B": "tru"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "tru" into bool`)},
    	{in: `{"B": "False"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "False" into bool`)},
    	{in: `{"B": "null"}`, ptr: new(B), out: B{false}},
    	{in: `{"B": "nul"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "nul" into bool`)},
    
    func TestMarshal(t *testing.T) {
    	b, err := Marshal(allValue)
    	if err != nil {
    		t.Fatalf("Marshal allValue: %v", err)
    	}
    	if string(b) != allValueCompact {
    		t.Errorf("Marshal allValueCompact")
    		diff(t, b, []byte(allValueCompact))
    		return
    	}
    
    	b, err = Marshal(pallValue)
    	if err != nil {
    		t.Fatalf("Marshal pallValue: %v", err)
    	}
    	if string(b) != pallValueCompact {
    		t.Errorf("Marshal pallValueCompact")
    		diff(t, b, []byte(pallValueCompact))
    		return
    	}
    
    var badUTF8 = []struct {
    	in, out string
    }{
    	{"hello\xffworld", `"hello\ufffdworld"`},
    	{"", `""`},
    	{"\xff", `"\ufffd"`},
    	{"\xff\xff", `"\ufffd\ufffd"`},
    	{"a\xffb", `"a\ufffdb"`},
    	{"\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"日本\ufffd\ufffd\ufffd"`},
    }
    
    
    Russ Cox's avatar
    Russ Cox committed
    func TestMarshalBadUTF8(t *testing.T) {
    
    	for _, tt := range badUTF8 {
    		b, err := Marshal(tt.in)
    		if string(b) != tt.out || err != nil {
    			t.Errorf("Marshal(%q) = %#q, %v, want %#q, nil", tt.in, b, err, tt.out)
    		}
    
    func TestMarshalNumberZeroVal(t *testing.T) {
    	var n Number
    	out, err := Marshal(n)
    	if err != nil {
    		t.Fatal(err)
    	}
    	outStr := string(out)
    	if outStr != "0" {
    		t.Fatalf("Invalid zero val for Number: %q", outStr)
    	}
    }
    
    
    func TestMarshalEmbeds(t *testing.T) {
    	top := &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},
    		},
    
    	}
    	b, err := Marshal(top)
    	if err != nil {
    		t.Fatal(err)
    	}
    
    	want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"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}"
    
    	if string(b) != want {
    		t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want)
    	}
    }
    
    
    func TestUnmarshal(t *testing.T) {
    	for i, tt := range unmarshalTests {
    
    		in := []byte(tt.in)
    		if err := checkValid(in, &scan); err != nil {
    
    			if !reflect.DeepEqual(err, tt.err) {
    
    				t.Errorf("#%d: checkValid: %#v", i, err)
    
    				continue
    			}
    		}
    		if tt.ptr == nil {
    
    		// v = new(right-type)
    
    Russ Cox's avatar
    Russ Cox committed
    		v := reflect.New(reflect.TypeOf(tt.ptr).Elem())
    
    		dec := NewDecoder(bytes.NewReader(in))
    
    		if tt.useNumber {
    			dec.UseNumber()
    		}
    		if err := dec.Decode(v.Interface()); !reflect.DeepEqual(err, tt.err) {
    
    			t.Errorf("#%d: %v, want %v", i, err, tt.err)
    			continue
    		} else if err != nil {
    
    			continue
    		}
    		if !reflect.DeepEqual(v.Elem().Interface(), tt.out) {
    			t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out)
    			data, _ := Marshal(v.Elem().Interface())
    			println(string(data))
    			data, _ = Marshal(tt.out)
    			println(string(data))
    			continue
    		}
    
    		// Check round trip also decodes correctly.
    
    		if tt.err == nil {
    			enc, err := Marshal(v.Interface())
    			if err != nil {
    				t.Errorf("#%d: error re-marshaling: %v", i, err)
    				continue
    			}
    
    			if tt.golden && !bytes.Equal(enc, in) {
    				t.Errorf("#%d: remarshal mismatch:\nhave: %s\nwant: %s", i, enc, in)
    			}
    
    			vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
    
    			dec = NewDecoder(bytes.NewReader(enc))
    
    			if tt.useNumber {
    				dec.UseNumber()
    			}
    			if err := dec.Decode(vv.Interface()); err != nil {
    
    				t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err)
    
    				continue
    			}
    			if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) {
    				t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface())
    
    				t.Errorf("     In: %q", strings.Map(noSpace, string(in)))
    				t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc)))
    
    func TestUnmarshalMarshal(t *testing.T) {
    
    	initBig()
    
    	var v interface{}
    	if err := Unmarshal(jsonBig, &v); err != nil {
    		t.Fatalf("Unmarshal: %v", err)
    	}
    	b, err := Marshal(v)
    	if err != nil {
    		t.Fatalf("Marshal: %v", err)
    	}
    
    	if !bytes.Equal(jsonBig, b) {
    
    		t.Errorf("Marshal jsonBig")
    		diff(t, b, jsonBig)
    		return
    	}
    
    var numberTests = []struct {
    	in       string
    	i        int64
    	intErr   string
    	f        float64
    	floatErr string
    }{
    	{in: "-1.23e1", intErr: "strconv.ParseInt: parsing \"-1.23e1\": invalid syntax", f: -1.23e1},
    	{in: "-12", i: -12, f: -12.0},
    	{in: "1e1000", intErr: "strconv.ParseInt: parsing \"1e1000\": invalid syntax", floatErr: "strconv.ParseFloat: parsing \"1e1000\": value out of range"},
    }
    
    // Independent of Decode, basic coverage of the accessors in Number
    func TestNumberAccessors(t *testing.T) {
    	for _, tt := range numberTests {
    		n := Number(tt.in)
    		if s := n.String(); s != tt.in {
    			t.Errorf("Number(%q).String() is %q", tt.in, s)
    		}
    		if i, err := n.Int64(); err == nil && tt.intErr == "" && i != tt.i {
    			t.Errorf("Number(%q).Int64() is %d", tt.in, i)
    		} else if (err == nil && tt.intErr != "") || (err != nil && err.Error() != tt.intErr) {
    			t.Errorf("Number(%q).Int64() wanted error %q but got: %v", tt.in, tt.intErr, err)
    		}
    		if f, err := n.Float64(); err == nil && tt.floatErr == "" && f != tt.f {
    			t.Errorf("Number(%q).Float64() is %g", tt.in, f)
    		} else if (err == nil && tt.floatErr != "") || (err != nil && err.Error() != tt.floatErr) {