2010-04-21 16:40:53 -07:00
|
|
|
// Copyright 2010 The Go Authors. All rights reserved.
|
2009-11-30 13:55:09 -08:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package json
|
|
|
|
|
|
|
|
|
|
import (
|
2010-04-21 16:40:53 -07:00
|
|
|
"bytes"
|
2013-08-14 14:56:07 -04:00
|
|
|
"encoding"
|
2016-03-08 12:41:35 -08:00
|
|
|
"errors"
|
2011-12-15 10:02:47 -08:00
|
|
|
"fmt"
|
2012-09-10 23:31:40 -04:00
|
|
|
"image"
|
2016-04-13 16:51:25 -07:00
|
|
|
"math"
|
2016-10-12 16:54:02 -04:00
|
|
|
"math/big"
|
2015-07-14 19:31:44 -04:00
|
|
|
"net"
|
2009-12-15 15:35:38 -08:00
|
|
|
"reflect"
|
2016-04-13 16:51:25 -07:00
|
|
|
"strconv"
|
2010-04-21 16:40:53 -07:00
|
|
|
"strings"
|
2009-12-15 15:35:38 -08:00
|
|
|
"testing"
|
2013-02-14 14:46:15 -05:00
|
|
|
"time"
|
2009-11-30 13:55:09 -08:00
|
|
|
)
|
|
|
|
|
|
2010-05-11 14:38:55 -07:00
|
|
|
type T struct {
|
|
|
|
|
X string
|
|
|
|
|
Y int
|
2011-09-15 08:09:43 +10:00
|
|
|
Z int `json:"-"`
|
2010-05-11 14:38:55 -07:00
|
|
|
}
|
|
|
|
|
|
2012-05-01 11:37:44 +10:00
|
|
|
type U struct {
|
|
|
|
|
Alphabet string `json:"alpha"`
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-25 17:36:09 -04:00
|
|
|
type V struct {
|
|
|
|
|
F1 interface{}
|
|
|
|
|
F2 int32
|
|
|
|
|
F3 Number
|
2016-01-18 16:26:05 +01:00
|
|
|
F4 *VOuter
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type VOuter struct {
|
|
|
|
|
V V
|
2012-06-25 17:36:09 -04:00
|
|
|
}
|
|
|
|
|
|
2018-09-03 11:20:23 +01:00
|
|
|
type W struct {
|
|
|
|
|
S SS
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-28 01:44:09 +07:00
|
|
|
type P struct {
|
|
|
|
|
PP PP
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type PP struct {
|
encoding/json: fix performance regression in the decoder
In golang.org/cl/145218, a feature was added where the JSON decoder
would keep track of the entire path to a field when reporting an
UnmarshalTypeError.
However, we all failed to check if this affected the benchmarks - myself
included, as a reviewer. Below are the numbers comparing the CL's parent
with itself, once it was merged:
name old time/op new time/op delta
CodeDecoder-8 12.9ms ± 1% 28.2ms ± 2% +119.33% (p=0.002 n=6+6)
name old speed new speed delta
CodeDecoder-8 151MB/s ± 1% 69MB/s ± 3% -54.40% (p=0.002 n=6+6)
name old alloc/op new alloc/op delta
CodeDecoder-8 2.74MB ± 0% 109.39MB ± 0% +3891.83% (p=0.002 n=6+6)
name old allocs/op new allocs/op delta
CodeDecoder-8 77.5k ± 0% 168.5k ± 0% +117.30% (p=0.004 n=6+5)
The reason why the decoder got twice as slow is because it now allocated
~40x as many objects, which puts a lot of pressure on the garbage
collector.
The reason is that the CL concatenated strings every time a nested field
was decoded. In other words, practically every field generated garbage
when decoded. This is hugely wasteful, especially considering that the
vast majority of JSON decoding inputs won't return UnmarshalTypeError.
Instead, use a stack of fields, and make sure to always use the same
backing array, to ensure we only need to grow the slice to the maximum
depth once.
The original CL also introduced a bug. The field stack string wasn't
reset to its original state when reaching "d.opcode == scanEndObject",
so the last field in a decoded struct could leak. For example, an added
test decodes a list of structs, and encoding/json before this CL would
fail:
got: cannot unmarshal string into Go struct field T.Ts.Y.Y.Y of type int
want: cannot unmarshal string into Go struct field T.Ts.Y of type int
To fix that, simply reset the stack after decoding every field, even if
it's the last.
Below is the original performance versus this CL. There's a tiny
performance hit, probably due to the append for every decoded field, but
at least we're back to the usual ~150MB/s.
name old time/op new time/op delta
CodeDecoder-8 12.9ms ± 1% 13.0ms ± 1% +1.25% (p=0.009 n=6+6)
name old speed new speed delta
CodeDecoder-8 151MB/s ± 1% 149MB/s ± 1% -1.24% (p=0.009 n=6+6)
name old alloc/op new alloc/op delta
CodeDecoder-8 2.74MB ± 0% 2.74MB ± 0% +0.00% (p=0.002 n=6+6)
name old allocs/op new allocs/op delta
CodeDecoder-8 77.5k ± 0% 77.5k ± 0% +0.00% (p=0.002 n=6+6)
Finally, make all of these benchmarks report allocs by default. The
decoder ones are pretty sensitive to generated garbage, so ReportAllocs
would have made the performance regression more obvious.
Change-Id: I67b50f86b2e72f55539429450c67bfb1a9464b67
Reviewed-on: https://go-review.googlesource.com/c/go/+/167978
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2019-03-17 22:45:30 +00:00
|
|
|
T T
|
|
|
|
|
Ts []T
|
2018-10-28 01:44:09 +07:00
|
|
|
}
|
|
|
|
|
|
2018-09-03 11:20:23 +01:00
|
|
|
type SS string
|
|
|
|
|
|
|
|
|
|
func (*SS) UnmarshalJSON(data []byte) error {
|
|
|
|
|
return &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(SS(""))}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 15:00:21 -07:00
|
|
|
// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and
|
2012-06-25 17:36:09 -04:00
|
|
|
// 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")},
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-28 14:40:23 -04:00
|
|
|
type tx struct {
|
|
|
|
|
x int
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-13 16:51:25 -07:00
|
|
|
type u8 uint8
|
|
|
|
|
|
2010-11-08 15:33:00 -08:00
|
|
|
// A type that can unmarshal itself.
|
|
|
|
|
|
|
|
|
|
type unmarshaler struct {
|
|
|
|
|
T bool
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-01 22:04:37 -04:00
|
|
|
func (u *unmarshaler) UnmarshalJSON(b []byte) error {
|
2013-08-14 14:56:07 -04:00
|
|
|
*u = unmarshaler{true} // All we need to see that UnmarshalJSON is called.
|
2010-11-08 15:33:00 -08:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-10 09:26:51 -04:00
|
|
|
type ustruct struct {
|
|
|
|
|
M unmarshaler
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-14 14:56:07 -04:00
|
|
|
type unmarshalerText struct {
|
2016-03-08 12:41:35 -08:00
|
|
|
A, B string
|
2013-08-14 14:56:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// needed for re-marshaling tests
|
2016-03-08 12:41:35 -08:00
|
|
|
func (u unmarshalerText) MarshalText() ([]byte, error) {
|
|
|
|
|
return []byte(u.A + ":" + u.B), nil
|
2013-08-14 14:56:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *unmarshalerText) UnmarshalText(b []byte) error {
|
2017-09-21 19:23:51 +02:00
|
|
|
pos := bytes.IndexByte(b, ':')
|
2016-03-08 12:41:35 -08:00
|
|
|
if pos == -1 {
|
|
|
|
|
return errors.New("missing separator")
|
|
|
|
|
}
|
|
|
|
|
u.A, u.B = string(b[:pos]), string(b[pos+1:])
|
2013-08-14 14:56:07 -04:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _ encoding.TextUnmarshaler = (*unmarshalerText)(nil)
|
|
|
|
|
|
|
|
|
|
type ustructText struct {
|
|
|
|
|
M unmarshalerText
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-13 16:51:25 -07:00
|
|
|
// 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)
|
|
|
|
|
|
2010-11-08 15:33:00 -08:00
|
|
|
var (
|
|
|
|
|
umtrue = unmarshaler{true}
|
2011-09-06 16:04:55 -07:00
|
|
|
umslice = []unmarshaler{{true}}
|
2011-08-10 09:26:51 -04:00
|
|
|
umstruct = ustruct{unmarshaler{true}}
|
2013-08-14 14:56:07 -04:00
|
|
|
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
umtrueXY = unmarshalerText{"x", "y"}
|
|
|
|
|
umsliceXY = []unmarshalerText{{"x", "y"}}
|
|
|
|
|
umstructXY = ustructText{unmarshalerText{"x", "y"}}
|
2016-03-08 12:41:35 -08:00
|
|
|
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
ummapXY = map[unmarshalerText]bool{{"x", "y"}: true}
|
2010-11-08 15:33:00 -08:00
|
|
|
)
|
|
|
|
|
|
2012-09-10 23:31:40 -04:00
|
|
|
// 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
|
2015-08-28 10:17:05 +02:00
|
|
|
embed // contains exported field
|
2012-09-10 23:31:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-28 10:17:05 +02:00
|
|
|
type embed struct {
|
|
|
|
|
Q int
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-10 23:31:40 -04:00
|
|
|
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"`
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-09 19:11:05 -04:00
|
|
|
type XYZ struct {
|
|
|
|
|
X interface{}
|
|
|
|
|
Y interface{}
|
|
|
|
|
Z interface{}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-11 14:43:47 +01:00
|
|
|
type unexportedWithMethods struct{}
|
|
|
|
|
|
|
|
|
|
func (unexportedWithMethods) F() {}
|
|
|
|
|
|
2016-05-23 12:21:57 -04:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-28 15:56:10 +00:00
|
|
|
type mapStringToStringData struct {
|
|
|
|
|
Data map[string]string `json:"data"`
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-23 12:21:57 -04:00
|
|
|
type unmarshalTest struct {
|
2017-10-31 13:16:38 -07:00
|
|
|
in string
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
ptr interface{} // new(type)
|
2017-10-31 13:16:38 -07:00
|
|
|
out interface{}
|
|
|
|
|
err error
|
|
|
|
|
useNumber bool
|
|
|
|
|
golden bool
|
|
|
|
|
disallowUnknownFields bool
|
2016-05-23 12:21:57 -04:00
|
|
|
}
|
|
|
|
|
|
2016-10-12 15:55:02 -04:00
|
|
|
type B struct {
|
|
|
|
|
B bool `json:",string"`
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
var unmarshalTests = []unmarshalTest{
|
|
|
|
|
// basic types
|
2012-05-29 18:02:40 -04:00
|
|
|
{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)},
|
2012-06-25 17:36:09 -04:00
|
|
|
{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},
|
2012-05-29 18:02:40 -04:00
|
|
|
{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},
|
2016-01-18 16:26:05 +01:00
|
|
|
{in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7, "T", "X"}},
|
2018-08-28 15:56:10 +00:00
|
|
|
{in: `{"X": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeOf(""), 8, "T", "X"}}, {in: `{"x": 1}`, ptr: new(tx), out: tx{}},
|
2013-01-22 17:49:07 -05:00
|
|
|
{in: `{"x": 1}`, ptr: new(tx), out: tx{}},
|
2017-10-31 13:16:38 -07:00
|
|
|
{in: `{"x": 1}`, ptr: new(tx), err: fmt.Errorf("json: unknown field \"x\""), disallowUnknownFields: true},
|
2018-09-03 11:20:23 +01:00
|
|
|
{in: `{"S": 23}`, ptr: new(W), out: W{}, err: &UnmarshalTypeError{"number", reflect.TypeOf(SS("")), 0, "W", "S"}},
|
2012-06-25 17:36:09 -04:00
|
|
|
{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},
|
2009-11-30 13:55:09 -08:00
|
|
|
|
2013-01-10 17:58:45 -08:00
|
|
|
// 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"},
|
|
|
|
|
|
2011-09-15 08:09:43 +10:00
|
|
|
// Z has a "-" tag.
|
2012-05-29 18:02:40 -04:00
|
|
|
{in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}},
|
2017-10-31 13:16:38 -07:00
|
|
|
{in: `{"Y": 1, "Z": 2}`, ptr: new(T), err: fmt.Errorf("json: unknown field \"Z\""), disallowUnknownFields: true},
|
2011-09-15 08:09:43 +10:00
|
|
|
|
2012-05-29 18:02:40 -04:00
|
|
|
{in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}},
|
2017-10-31 13:16:38 -07:00
|
|
|
{in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},
|
2012-05-29 18:02:40 -04:00
|
|
|
{in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}},
|
|
|
|
|
{in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}},
|
2017-10-31 13:16:38 -07:00
|
|
|
{in: `{"alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},
|
2012-05-01 11:37:44 +10:00
|
|
|
|
2010-08-03 17:05:00 -07:00
|
|
|
// syntax errors
|
2012-05-29 18:02:40 -04:00
|
|
|
{in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}},
|
|
|
|
|
{in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}},
|
2012-06-25 17:36:09 -04:00
|
|
|
{in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true},
|
2018-09-11 22:09:00 +02:00
|
|
|
{in: `[2, 3`, err: &SyntaxError{msg: "unexpected end of JSON input", Offset: 5}},
|
2019-07-03 00:37:05 +02:00
|
|
|
{in: `{"F3": -}`, ptr: new(V), out: V{F3: Number("-")}, err: &SyntaxError{msg: "invalid character '}' in numeric literal", Offset: 9}},
|
2011-12-19 15:32:06 -05:00
|
|
|
|
2013-01-10 17:58:45 -08:00
|
|
|
// 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}},
|
|
|
|
|
|
2011-12-19 15:32:06 -05:00
|
|
|
// array tests
|
2012-05-29 18:02:40 -04:00
|
|
|
{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}},
|
2018-09-11 22:09:00 +02:00
|
|
|
{in: `[1, 2, 3]`, ptr: new(MustNotUnmarshalJSON), err: errors.New("MustNotUnmarshalJSON was used")},
|
2010-08-03 17:05:00 -07:00
|
|
|
|
2013-01-30 09:10:32 -08:00
|
|
|
// 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)}},
|
|
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
// composite tests
|
2012-05-29 18:02:40 -04:00
|
|
|
{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},
|
2010-11-08 15:33:00 -08:00
|
|
|
|
|
|
|
|
// unmarshal interface test
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
{in: `{"T":false}`, ptr: new(unmarshaler), out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called
|
|
|
|
|
{in: `{"T":false}`, ptr: new(*unmarshaler), out: &umtrue},
|
|
|
|
|
{in: `[{"T":false}]`, ptr: new([]unmarshaler), out: umslice},
|
|
|
|
|
{in: `[{"T":false}]`, ptr: new(*[]unmarshaler), out: &umslice},
|
|
|
|
|
{in: `{"M":{"T":"x:y"}}`, ptr: new(ustruct), out: umstruct},
|
2012-09-10 23:31:40 -04:00
|
|
|
|
2013-08-14 14:56:07 -04:00
|
|
|
// UnmarshalText interface test
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
{in: `"x:y"`, ptr: new(unmarshalerText), out: umtrueXY},
|
|
|
|
|
{in: `"x:y"`, ptr: new(*unmarshalerText), out: &umtrueXY},
|
|
|
|
|
{in: `["x:y"]`, ptr: new([]unmarshalerText), out: umsliceXY},
|
|
|
|
|
{in: `["x:y"]`, ptr: new(*[]unmarshalerText), out: &umsliceXY},
|
|
|
|
|
{in: `{"M":"x:y"}`, ptr: new(ustructText), out: umstructXY},
|
2016-03-08 12:41:35 -08:00
|
|
|
|
2016-04-13 16:51:25 -07:00
|
|
|
// 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),
|
2016-01-18 16:26:05 +01:00
|
|
|
err: &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeOf(0), Offset: 2},
|
2016-04-13 16:51:25 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `{"256":"abc"}`,
|
|
|
|
|
ptr: new(map[uint8]string),
|
2016-01-18 16:26:05 +01:00
|
|
|
err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeOf(uint8(0)), Offset: 2},
|
2016-04-13 16:51:25 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `{"128":"abc"}`,
|
|
|
|
|
ptr: new(map[int8]string),
|
2016-01-18 16:26:05 +01:00
|
|
|
err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeOf(int8(0)), Offset: 2},
|
2016-04-13 16:51:25 -07:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `{"-1":"abc"}`,
|
|
|
|
|
ptr: new(map[uint8]string),
|
2016-01-18 16:26:05 +01:00
|
|
|
err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeOf(uint8(0)), Offset: 2},
|
2016-04-13 16:51:25 -07:00
|
|
|
},
|
encoding/json: fix "data changed underfoot?" panic
Given a program as follows:
data := []byte(`{"F": {
"a": 2,
"3": 4
}}`)
json.Unmarshal(data, &map[string]map[int]int{})
The JSON package should error, as "a" is not a valid integer. However,
we'd encounter a panic:
panic: JSON decoder out of sync - data changing underfoot?
The reason was that decodeState.object would return a nil error on
encountering the invalid map key string, while saving the key type error
for later. This broke if we were inside another object, as we would
abruptly end parsing the nested object, leaving the decoder in an
unexpected state.
To fix this, simply avoid storing the map element and continue decoding
the object, to leave the decoder state exactly as if we hadn't seen an
invalid key type.
This affected both signed and unsigned integer keys, so fix both and add
two test cases.
Updates #28189.
Change-Id: I8a6204cc3ff9fb04ed769df7a20a824c8b94faff
Reviewed-on: https://go-review.googlesource.com/c/142518
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-10-16 12:04:55 +01:00
|
|
|
{
|
|
|
|
|
in: `{"F":{"a":2,"3":4}}`,
|
|
|
|
|
ptr: new(map[string]map[int]int),
|
|
|
|
|
err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeOf(int(0)), Offset: 7},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `{"F":{"a":2,"3":4}}`,
|
|
|
|
|
ptr: new(map[string]map[uint]int),
|
|
|
|
|
err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeOf(uint(0)), Offset: 7},
|
|
|
|
|
},
|
2016-04-13 16:51:25 -07:00
|
|
|
|
|
|
|
|
// Map keys can be encoding.TextUnmarshalers.
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
{in: `{"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},
|
2016-03-08 12:41:35 -08:00
|
|
|
// If multiple values for the same key exists, only the most recent value is used.
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
{in: `{"x:y":false,"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},
|
2015-07-14 21:32:47 -04:00
|
|
|
|
2012-09-10 23:31:40 -04:00
|
|
|
{
|
|
|
|
|
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,
|
2015-08-28 10:17:05 +02:00
|
|
|
"Z": 17,
|
|
|
|
|
"Q": 18
|
2012-09-10 23:31:40 -04:00
|
|
|
}`,
|
|
|
|
|
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},
|
|
|
|
|
},
|
2015-08-28 10:17:05 +02:00
|
|
|
embed: embed{
|
|
|
|
|
Q: 18,
|
|
|
|
|
},
|
2012-09-10 23:31:40 -04:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
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}}},
|
|
|
|
|
},
|
2017-10-31 13:16:38 -07:00
|
|
|
{
|
go/printer, gofmt: tuned table alignment for better results
The go/printer (and thus gofmt) uses a heuristic to determine
whether to break alignment between elements of an expression
list which is spread across multiple lines. The heuristic only
kicked in if the entry sizes (character length) was above a
certain threshold (20) and the ratio between the previous and
current entry size was above a certain value (4).
This heuristic worked reasonably most of the time, but also
led to unfortunate breaks in many cases where a single entry
was suddenly much smaller (or larger) then the previous one.
The behavior of gofmt was sufficiently mysterious in some of
these situations that many issues were filed against it.
The simplest solution to address this problem is to remove
the heuristic altogether and have a programmer introduce
empty lines to force different alignments if it improves
readability. The problem with that approach is that the
places where it really matters, very long tables with many
(hundreds, or more) entries, may be machine-generated and
not "post-processed" by a human (e.g., unicode/utf8/tables.go).
If a single one of those entries is overlong, the result
would be that the alignment would force all comments or
values in key:value pairs to be adjusted to that overlong
value, making the table hard to read (e.g., that entry may
not even be visible on screen and all other entries seem
spaced out too wide).
Instead, we opted for a slightly improved heuristic that
behaves much better for "normal", human-written code.
1) The threshold is increased from 20 to 40. This disables
the heuristic for many common cases yet even if the alignment
is not "ideal", 40 is not that many characters per line with
todays screens, making it very likely that the entire line
remains "visible" in an editor.
2) Changed the heuristic to not simply look at the size ratio
between current and previous line, but instead considering the
geometric mean of the sizes of the previous (aligned) lines.
This emphasizes the "overall picture" of the previous lines,
rather than a single one (which might be an outlier).
3) Changed the ratio from 4 to 2.5. Now that we ignore sizes
below 40, a ratio of 4 would mean that a new entry would have
to be 4 times bigger (160) or smaller (10) before alignment
would be broken. A ratio of 2.5 seems more sensible.
Applied updated gofmt to all of src and misc. Also tested
against several former issues that complained about this
and verified that the output for the given examples is
satisfactory (added respective test cases).
Some of the files changed because they were not gofmt-ed
in the first place.
For #644.
For #7335.
For #10392.
(and probably more related issues)
Fixes #22852.
Change-Id: I5e48b3d3b157a5cf2d649833b7297b33f43a6f6e
2018-04-03 17:05:47 -07:00
|
|
|
in: `{"X": 1,"Y":2}`,
|
|
|
|
|
ptr: new(S5),
|
|
|
|
|
err: fmt.Errorf("json: unknown field \"X\""),
|
2017-10-31 13:16:38 -07:00
|
|
|
disallowUnknownFields: true,
|
|
|
|
|
},
|
2012-09-10 23:31:40 -04:00
|
|
|
{
|
|
|
|
|
in: `{"X": 1,"Y":2}`,
|
|
|
|
|
ptr: new(S10),
|
|
|
|
|
out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}},
|
|
|
|
|
},
|
2017-10-31 13:16:38 -07:00
|
|
|
{
|
go/printer, gofmt: tuned table alignment for better results
The go/printer (and thus gofmt) uses a heuristic to determine
whether to break alignment between elements of an expression
list which is spread across multiple lines. The heuristic only
kicked in if the entry sizes (character length) was above a
certain threshold (20) and the ratio between the previous and
current entry size was above a certain value (4).
This heuristic worked reasonably most of the time, but also
led to unfortunate breaks in many cases where a single entry
was suddenly much smaller (or larger) then the previous one.
The behavior of gofmt was sufficiently mysterious in some of
these situations that many issues were filed against it.
The simplest solution to address this problem is to remove
the heuristic altogether and have a programmer introduce
empty lines to force different alignments if it improves
readability. The problem with that approach is that the
places where it really matters, very long tables with many
(hundreds, or more) entries, may be machine-generated and
not "post-processed" by a human (e.g., unicode/utf8/tables.go).
If a single one of those entries is overlong, the result
would be that the alignment would force all comments or
values in key:value pairs to be adjusted to that overlong
value, making the table hard to read (e.g., that entry may
not even be visible on screen and all other entries seem
spaced out too wide).
Instead, we opted for a slightly improved heuristic that
behaves much better for "normal", human-written code.
1) The threshold is increased from 20 to 40. This disables
the heuristic for many common cases yet even if the alignment
is not "ideal", 40 is not that many characters per line with
todays screens, making it very likely that the entire line
remains "visible" in an editor.
2) Changed the heuristic to not simply look at the size ratio
between current and previous line, but instead considering the
geometric mean of the sizes of the previous (aligned) lines.
This emphasizes the "overall picture" of the previous lines,
rather than a single one (which might be an outlier).
3) Changed the ratio from 4 to 2.5. Now that we ignore sizes
below 40, a ratio of 4 would mean that a new entry would have
to be 4 times bigger (160) or smaller (10) before alignment
would be broken. A ratio of 2.5 seems more sensible.
Applied updated gofmt to all of src and misc. Also tested
against several former issues that complained about this
and verified that the output for the given examples is
satisfactory (added respective test cases).
Some of the files changed because they were not gofmt-ed
in the first place.
For #644.
For #7335.
For #10392.
(and probably more related issues)
Fixes #22852.
Change-Id: I5e48b3d3b157a5cf2d649833b7297b33f43a6f6e
2018-04-03 17:05:47 -07:00
|
|
|
in: `{"X": 1,"Y":2}`,
|
|
|
|
|
ptr: new(S10),
|
|
|
|
|
err: fmt.Errorf("json: unknown field \"X\""),
|
2017-10-31 13:16:38 -07:00
|
|
|
disallowUnknownFields: true,
|
|
|
|
|
},
|
2013-02-14 14:56:01 -05:00
|
|
|
|
|
|
|
|
// 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",
|
|
|
|
|
},
|
2014-10-01 16:24:17 -07:00
|
|
|
|
2016-03-08 12:41:35 -08:00
|
|
|
// Used to be issue 8305, but time.Time implements encoding.TextUnmarshaler so this works now.
|
2014-10-01 16:24:17 -07:00
|
|
|
{
|
|
|
|
|
in: `{"2009-11-10T23:00:00Z": "hello world"}`,
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
ptr: new(map[time.Time]string),
|
2016-03-08 12:41:35 -08:00
|
|
|
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"}`,
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
ptr: new(map[Point]string),
|
2016-01-18 16:26:05 +01:00
|
|
|
err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[Point]string{}), Offset: 1},
|
2016-03-08 12:41:35 -08:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `{"asdf": "hello world"}`,
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
ptr: new(map[unmarshaler]string),
|
2016-01-18 16:26:05 +01:00
|
|
|
err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[unmarshaler]string{}), Offset: 1},
|
2014-10-01 16:24:17 -07:00
|
|
|
},
|
2016-05-23 12:21:57 -04:00
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
|
},
|
2016-10-05 11:26:04 -04:00
|
|
|
|
|
|
|
|
{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},
|
2016-01-18 16:26:05 +01:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
in: `{"V": {"F2": "hello"}}`,
|
|
|
|
|
ptr: new(VOuter),
|
|
|
|
|
err: &UnmarshalTypeError{
|
|
|
|
|
Value: "string",
|
|
|
|
|
Struct: "V",
|
2018-10-28 01:44:09 +07:00
|
|
|
Field: "V.F2",
|
2016-01-18 16:26:05 +01:00
|
|
|
Type: reflect.TypeOf(int32(0)),
|
|
|
|
|
Offset: 20,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `{"V": {"F4": {}, "F2": "hello"}}`,
|
|
|
|
|
ptr: new(VOuter),
|
|
|
|
|
err: &UnmarshalTypeError{
|
|
|
|
|
Value: "string",
|
|
|
|
|
Struct: "V",
|
2018-10-28 01:44:09 +07:00
|
|
|
Field: "V.F2",
|
2016-01-18 16:26:05 +01:00
|
|
|
Type: reflect.TypeOf(int32(0)),
|
|
|
|
|
Offset: 30,
|
|
|
|
|
},
|
|
|
|
|
},
|
2016-10-12 15:55:02 -04:00
|
|
|
|
|
|
|
|
// 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`)},
|
2018-09-11 22:09:00 +02:00
|
|
|
{in: `{"B": [2, 3]}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal unquoted value into bool`)},
|
2017-10-31 13:16:38 -07:00
|
|
|
|
|
|
|
|
// additional tests for disallowUnknownFields
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
}`,
|
go/printer, gofmt: tuned table alignment for better results
The go/printer (and thus gofmt) uses a heuristic to determine
whether to break alignment between elements of an expression
list which is spread across multiple lines. The heuristic only
kicked in if the entry sizes (character length) was above a
certain threshold (20) and the ratio between the previous and
current entry size was above a certain value (4).
This heuristic worked reasonably most of the time, but also
led to unfortunate breaks in many cases where a single entry
was suddenly much smaller (or larger) then the previous one.
The behavior of gofmt was sufficiently mysterious in some of
these situations that many issues were filed against it.
The simplest solution to address this problem is to remove
the heuristic altogether and have a programmer introduce
empty lines to force different alignments if it improves
readability. The problem with that approach is that the
places where it really matters, very long tables with many
(hundreds, or more) entries, may be machine-generated and
not "post-processed" by a human (e.g., unicode/utf8/tables.go).
If a single one of those entries is overlong, the result
would be that the alignment would force all comments or
values in key:value pairs to be adjusted to that overlong
value, making the table hard to read (e.g., that entry may
not even be visible on screen and all other entries seem
spaced out too wide).
Instead, we opted for a slightly improved heuristic that
behaves much better for "normal", human-written code.
1) The threshold is increased from 20 to 40. This disables
the heuristic for many common cases yet even if the alignment
is not "ideal", 40 is not that many characters per line with
todays screens, making it very likely that the entire line
remains "visible" in an editor.
2) Changed the heuristic to not simply look at the size ratio
between current and previous line, but instead considering the
geometric mean of the sizes of the previous (aligned) lines.
This emphasizes the "overall picture" of the previous lines,
rather than a single one (which might be an outlier).
3) Changed the ratio from 4 to 2.5. Now that we ignore sizes
below 40, a ratio of 4 would mean that a new entry would have
to be 4 times bigger (160) or smaller (10) before alignment
would be broken. A ratio of 2.5 seems more sensible.
Applied updated gofmt to all of src and misc. Also tested
against several former issues that complained about this
and verified that the output for the given examples is
satisfactory (added respective test cases).
Some of the files changed because they were not gofmt-ed
in the first place.
For #644.
For #7335.
For #10392.
(and probably more related issues)
Fixes #22852.
Change-Id: I5e48b3d3b157a5cf2d649833b7297b33f43a6f6e
2018-04-03 17:05:47 -07:00
|
|
|
ptr: new(Top),
|
|
|
|
|
err: fmt.Errorf("json: unknown field \"extra\""),
|
2017-10-31 13:16:38 -07:00
|
|
|
disallowUnknownFields: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
}`,
|
go/printer, gofmt: tuned table alignment for better results
The go/printer (and thus gofmt) uses a heuristic to determine
whether to break alignment between elements of an expression
list which is spread across multiple lines. The heuristic only
kicked in if the entry sizes (character length) was above a
certain threshold (20) and the ratio between the previous and
current entry size was above a certain value (4).
This heuristic worked reasonably most of the time, but also
led to unfortunate breaks in many cases where a single entry
was suddenly much smaller (or larger) then the previous one.
The behavior of gofmt was sufficiently mysterious in some of
these situations that many issues were filed against it.
The simplest solution to address this problem is to remove
the heuristic altogether and have a programmer introduce
empty lines to force different alignments if it improves
readability. The problem with that approach is that the
places where it really matters, very long tables with many
(hundreds, or more) entries, may be machine-generated and
not "post-processed" by a human (e.g., unicode/utf8/tables.go).
If a single one of those entries is overlong, the result
would be that the alignment would force all comments or
values in key:value pairs to be adjusted to that overlong
value, making the table hard to read (e.g., that entry may
not even be visible on screen and all other entries seem
spaced out too wide).
Instead, we opted for a slightly improved heuristic that
behaves much better for "normal", human-written code.
1) The threshold is increased from 20 to 40. This disables
the heuristic for many common cases yet even if the alignment
is not "ideal", 40 is not that many characters per line with
todays screens, making it very likely that the entire line
remains "visible" in an editor.
2) Changed the heuristic to not simply look at the size ratio
between current and previous line, but instead considering the
geometric mean of the sizes of the previous (aligned) lines.
This emphasizes the "overall picture" of the previous lines,
rather than a single one (which might be an outlier).
3) Changed the ratio from 4 to 2.5. Now that we ignore sizes
below 40, a ratio of 4 would mean that a new entry would have
to be 4 times bigger (160) or smaller (10) before alignment
would be broken. A ratio of 2.5 seems more sensible.
Applied updated gofmt to all of src and misc. Also tested
against several former issues that complained about this
and verified that the output for the given examples is
satisfactory (added respective test cases).
Some of the files changed because they were not gofmt-ed
in the first place.
For #644.
For #7335.
For #10392.
(and probably more related issues)
Fixes #22852.
Change-Id: I5e48b3d3b157a5cf2d649833b7297b33f43a6f6e
2018-04-03 17:05:47 -07:00
|
|
|
ptr: new(Top),
|
|
|
|
|
err: fmt.Errorf("json: unknown field \"extra\""),
|
2017-10-31 13:16:38 -07:00
|
|
|
disallowUnknownFields: true,
|
|
|
|
|
},
|
2018-08-28 15:56:10 +00:00
|
|
|
// issue 26444
|
|
|
|
|
// UnmarshalTypeError without field & struct values
|
|
|
|
|
{
|
|
|
|
|
in: `{"data":{"test1": "bob", "test2": 123}}`,
|
|
|
|
|
ptr: new(mapStringToStringData),
|
|
|
|
|
err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(""), Offset: 37, Struct: "mapStringToStringData", Field: "data"},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `{"data":{"test1": 123, "test2": "bob"}}`,
|
|
|
|
|
ptr: new(mapStringToStringData),
|
|
|
|
|
err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(""), Offset: 21, Struct: "mapStringToStringData", Field: "data"},
|
|
|
|
|
},
|
2018-09-11 22:09:00 +02:00
|
|
|
|
|
|
|
|
// trying to decode JSON arrays or objects via TextUnmarshaler
|
|
|
|
|
{
|
|
|
|
|
in: `[1, 2, 3]`,
|
|
|
|
|
ptr: new(MustNotUnmarshalText),
|
|
|
|
|
err: &UnmarshalTypeError{Value: "array", Type: reflect.TypeOf(&MustNotUnmarshalText{}), Offset: 1},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `{"foo": "bar"}`,
|
|
|
|
|
ptr: new(MustNotUnmarshalText),
|
|
|
|
|
err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(&MustNotUnmarshalText{}), Offset: 1},
|
|
|
|
|
},
|
2018-10-28 01:44:09 +07:00
|
|
|
// #22369
|
|
|
|
|
{
|
|
|
|
|
in: `{"PP": {"T": {"Y": "bad-type"}}}`,
|
|
|
|
|
ptr: new(P),
|
|
|
|
|
err: &UnmarshalTypeError{
|
|
|
|
|
Value: "string",
|
|
|
|
|
Struct: "T",
|
|
|
|
|
Field: "PP.T.Y",
|
|
|
|
|
Type: reflect.TypeOf(int(0)),
|
|
|
|
|
Offset: 29,
|
|
|
|
|
},
|
|
|
|
|
},
|
encoding/json: fix performance regression in the decoder
In golang.org/cl/145218, a feature was added where the JSON decoder
would keep track of the entire path to a field when reporting an
UnmarshalTypeError.
However, we all failed to check if this affected the benchmarks - myself
included, as a reviewer. Below are the numbers comparing the CL's parent
with itself, once it was merged:
name old time/op new time/op delta
CodeDecoder-8 12.9ms ± 1% 28.2ms ± 2% +119.33% (p=0.002 n=6+6)
name old speed new speed delta
CodeDecoder-8 151MB/s ± 1% 69MB/s ± 3% -54.40% (p=0.002 n=6+6)
name old alloc/op new alloc/op delta
CodeDecoder-8 2.74MB ± 0% 109.39MB ± 0% +3891.83% (p=0.002 n=6+6)
name old allocs/op new allocs/op delta
CodeDecoder-8 77.5k ± 0% 168.5k ± 0% +117.30% (p=0.004 n=6+5)
The reason why the decoder got twice as slow is because it now allocated
~40x as many objects, which puts a lot of pressure on the garbage
collector.
The reason is that the CL concatenated strings every time a nested field
was decoded. In other words, practically every field generated garbage
when decoded. This is hugely wasteful, especially considering that the
vast majority of JSON decoding inputs won't return UnmarshalTypeError.
Instead, use a stack of fields, and make sure to always use the same
backing array, to ensure we only need to grow the slice to the maximum
depth once.
The original CL also introduced a bug. The field stack string wasn't
reset to its original state when reaching "d.opcode == scanEndObject",
so the last field in a decoded struct could leak. For example, an added
test decodes a list of structs, and encoding/json before this CL would
fail:
got: cannot unmarshal string into Go struct field T.Ts.Y.Y.Y of type int
want: cannot unmarshal string into Go struct field T.Ts.Y of type int
To fix that, simply reset the stack after decoding every field, even if
it's the last.
Below is the original performance versus this CL. There's a tiny
performance hit, probably due to the append for every decoded field, but
at least we're back to the usual ~150MB/s.
name old time/op new time/op delta
CodeDecoder-8 12.9ms ± 1% 13.0ms ± 1% +1.25% (p=0.009 n=6+6)
name old speed new speed delta
CodeDecoder-8 151MB/s ± 1% 149MB/s ± 1% -1.24% (p=0.009 n=6+6)
name old alloc/op new alloc/op delta
CodeDecoder-8 2.74MB ± 0% 2.74MB ± 0% +0.00% (p=0.002 n=6+6)
name old allocs/op new allocs/op delta
CodeDecoder-8 77.5k ± 0% 77.5k ± 0% +0.00% (p=0.002 n=6+6)
Finally, make all of these benchmarks report allocs by default. The
decoder ones are pretty sensitive to generated garbage, so ReportAllocs
would have made the performance regression more obvious.
Change-Id: I67b50f86b2e72f55539429450c67bfb1a9464b67
Reviewed-on: https://go-review.googlesource.com/c/go/+/167978
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2019-03-17 22:45:30 +00:00
|
|
|
{
|
|
|
|
|
in: `{"Ts": [{"Y": 1}, {"Y": 2}, {"Y": "bad-type"}]}`,
|
|
|
|
|
ptr: new(PP),
|
|
|
|
|
err: &UnmarshalTypeError{
|
|
|
|
|
Value: "string",
|
|
|
|
|
Struct: "T",
|
|
|
|
|
Field: "Ts.Y",
|
|
|
|
|
Type: reflect.TypeOf(int(0)),
|
|
|
|
|
Offset: 29,
|
|
|
|
|
},
|
|
|
|
|
},
|
2009-11-30 13:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
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
|
|
|
|
|
}
|
2009-11-30 13:55:09 -08:00
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
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
|
|
|
|
|
}
|
2009-11-30 13:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2013-07-12 20:40:50 -04:00
|
|
|
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"`},
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-13 15:51:11 -05:00
|
|
|
func TestMarshalBadUTF8(t *testing.T) {
|
2013-07-12 20:40:50 -04:00
|
|
|
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)
|
|
|
|
|
}
|
2010-12-13 15:51:11 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-25 17:36:09 -04:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
encoding/json: faster encoding
The old code was caching per-type struct field info. Instead,
cache type-specific encoding funcs, tailored for that
particular type to avoid unnecessary reflection at runtime.
Once the machine is built once, future encodings of that type
just run the func.
benchmark old ns/op new ns/op delta
BenchmarkCodeEncoder 48424939 36975320 -23.64%
benchmark old MB/s new MB/s speedup
BenchmarkCodeEncoder 40.07 52.48 1.31x
Additionally, the numbers seem stable now at ~52 MB/s, whereas
the numbers for the old code were all over the place: 11 MB/s,
40 MB/s, 13 MB/s, 39 MB/s, etc. In the benchmark above I compared
against the best I saw the old code do.
R=rsc, adg
CC=gobot, golang-dev, r
https://golang.org/cl/9129044
2013-08-09 09:46:47 -07:00
|
|
|
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},
|
|
|
|
|
},
|
2015-08-28 10:17:05 +02:00
|
|
|
embed: embed{
|
|
|
|
|
Q: 18,
|
|
|
|
|
},
|
encoding/json: faster encoding
The old code was caching per-type struct field info. Instead,
cache type-specific encoding funcs, tailored for that
particular type to avoid unnecessary reflection at runtime.
Once the machine is built once, future encodings of that type
just run the func.
benchmark old ns/op new ns/op delta
BenchmarkCodeEncoder 48424939 36975320 -23.64%
benchmark old MB/s new MB/s speedup
BenchmarkCodeEncoder 40.07 52.48 1.31x
Additionally, the numbers seem stable now at ~52 MB/s, whereas
the numbers for the old code were all over the place: 11 MB/s,
40 MB/s, 13 MB/s, 39 MB/s, etc. In the benchmark above I compared
against the best I saw the old code do.
R=rsc, adg
CC=gobot, golang-dev, r
https://golang.org/cl/9129044
2013-08-09 09:46:47 -07:00
|
|
|
}
|
|
|
|
|
b, err := Marshal(top)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2015-08-28 10:17:05 +02:00
|
|
|
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}"
|
encoding/json: faster encoding
The old code was caching per-type struct field info. Instead,
cache type-specific encoding funcs, tailored for that
particular type to avoid unnecessary reflection at runtime.
Once the machine is built once, future encodings of that type
just run the func.
benchmark old ns/op new ns/op delta
BenchmarkCodeEncoder 48424939 36975320 -23.64%
benchmark old MB/s new MB/s speedup
BenchmarkCodeEncoder 40.07 52.48 1.31x
Additionally, the numbers seem stable now at ~52 MB/s, whereas
the numbers for the old code were all over the place: 11 MB/s,
40 MB/s, 13 MB/s, 39 MB/s, etc. In the benchmark above I compared
against the best I saw the old code do.
R=rsc, adg
CC=gobot, golang-dev, r
https://golang.org/cl/9129044
2013-08-09 09:46:47 -07:00
|
|
|
if string(b) != want {
|
|
|
|
|
t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-08 17:52:08 +01:00
|
|
|
func equalError(a, b error) bool {
|
|
|
|
|
if a == nil {
|
|
|
|
|
return b == nil
|
|
|
|
|
}
|
|
|
|
|
if b == nil {
|
|
|
|
|
return a == nil
|
|
|
|
|
}
|
|
|
|
|
return a.Error() == b.Error()
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
func TestUnmarshal(t *testing.T) {
|
|
|
|
|
for i, tt := range unmarshalTests {
|
2011-04-15 08:14:34 -07:00
|
|
|
var scan scanner
|
2010-04-21 16:40:53 -07:00
|
|
|
in := []byte(tt.in)
|
|
|
|
|
if err := checkValid(in, &scan); err != nil {
|
2019-02-08 17:52:08 +01:00
|
|
|
if !equalError(err, tt.err) {
|
2011-04-15 08:14:34 -07:00
|
|
|
t.Errorf("#%d: checkValid: %#v", i, err)
|
2010-08-03 17:05:00 -07:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if tt.ptr == nil {
|
2010-04-21 16:40:53 -07:00
|
|
|
continue
|
|
|
|
|
}
|
2014-10-01 16:24:17 -07:00
|
|
|
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
typ := reflect.TypeOf(tt.ptr)
|
|
|
|
|
if typ.Kind() != reflect.Ptr {
|
|
|
|
|
t.Errorf("#%d: unmarshalTest.ptr %T is not a pointer type", i, tt.ptr)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
typ = typ.Elem()
|
|
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
// v = new(right-type)
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
v := reflect.New(typ)
|
|
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(tt.ptr, v.Interface()) {
|
|
|
|
|
// There's no reason for ptr to point to non-zero data,
|
|
|
|
|
// as we decode into new(right-type), so the data is
|
|
|
|
|
// discarded.
|
|
|
|
|
// This can easily mean tests that silently don't test
|
|
|
|
|
// what they should. To test decoding into existing
|
|
|
|
|
// data, see TestPrefilled.
|
|
|
|
|
t.Errorf("#%d: unmarshalTest.ptr %#v is not a pointer to a zero value", i, tt.ptr)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
encoding/json: faster encoding
The old code was caching per-type struct field info. Instead,
cache type-specific encoding funcs, tailored for that
particular type to avoid unnecessary reflection at runtime.
Once the machine is built once, future encodings of that type
just run the func.
benchmark old ns/op new ns/op delta
BenchmarkCodeEncoder 48424939 36975320 -23.64%
benchmark old MB/s new MB/s speedup
BenchmarkCodeEncoder 40.07 52.48 1.31x
Additionally, the numbers seem stable now at ~52 MB/s, whereas
the numbers for the old code were all over the place: 11 MB/s,
40 MB/s, 13 MB/s, 39 MB/s, etc. In the benchmark above I compared
against the best I saw the old code do.
R=rsc, adg
CC=gobot, golang-dev, r
https://golang.org/cl/9129044
2013-08-09 09:46:47 -07:00
|
|
|
dec := NewDecoder(bytes.NewReader(in))
|
2012-06-25 17:36:09 -04:00
|
|
|
if tt.useNumber {
|
|
|
|
|
dec.UseNumber()
|
|
|
|
|
}
|
2017-10-31 13:16:38 -07:00
|
|
|
if tt.disallowUnknownFields {
|
|
|
|
|
dec.DisallowUnknownFields()
|
|
|
|
|
}
|
2019-02-08 17:52:08 +01:00
|
|
|
if err := dec.Decode(v.Interface()); !equalError(err, tt.err) {
|
2014-10-01 16:24:17 -07:00
|
|
|
t.Errorf("#%d: %v, want %v", i, err, tt.err)
|
|
|
|
|
continue
|
|
|
|
|
} else if err != nil {
|
2010-04-21 16:40:53 -07:00
|
|
|
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
|
|
|
|
|
}
|
2012-05-29 18:02:40 -04:00
|
|
|
|
2016-05-23 12:21:57 -04:00
|
|
|
// Check round trip also decodes correctly.
|
2012-05-29 18:02:40 -04:00
|
|
|
if tt.err == nil {
|
|
|
|
|
enc, err := Marshal(v.Interface())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("#%d: error re-marshaling: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2016-05-23 12:21:57 -04:00
|
|
|
if tt.golden && !bytes.Equal(enc, in) {
|
|
|
|
|
t.Errorf("#%d: remarshal mismatch:\nhave: %s\nwant: %s", i, enc, in)
|
|
|
|
|
}
|
2012-05-29 18:02:40 -04:00
|
|
|
vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
|
encoding/json: faster encoding
The old code was caching per-type struct field info. Instead,
cache type-specific encoding funcs, tailored for that
particular type to avoid unnecessary reflection at runtime.
Once the machine is built once, future encodings of that type
just run the func.
benchmark old ns/op new ns/op delta
BenchmarkCodeEncoder 48424939 36975320 -23.64%
benchmark old MB/s new MB/s speedup
BenchmarkCodeEncoder 40.07 52.48 1.31x
Additionally, the numbers seem stable now at ~52 MB/s, whereas
the numbers for the old code were all over the place: 11 MB/s,
40 MB/s, 13 MB/s, 39 MB/s, etc. In the benchmark above I compared
against the best I saw the old code do.
R=rsc, adg
CC=gobot, golang-dev, r
https://golang.org/cl/9129044
2013-08-09 09:46:47 -07:00
|
|
|
dec = NewDecoder(bytes.NewReader(enc))
|
2012-06-25 17:36:09 -04:00
|
|
|
if tt.useNumber {
|
|
|
|
|
dec.UseNumber()
|
|
|
|
|
}
|
|
|
|
|
if err := dec.Decode(vv.Interface()); err != nil {
|
2013-08-14 14:56:07 -04:00
|
|
|
t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err)
|
2012-05-29 18:02:40 -04:00
|
|
|
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())
|
encoding/json: faster encoding
The old code was caching per-type struct field info. Instead,
cache type-specific encoding funcs, tailored for that
particular type to avoid unnecessary reflection at runtime.
Once the machine is built once, future encodings of that type
just run the func.
benchmark old ns/op new ns/op delta
BenchmarkCodeEncoder 48424939 36975320 -23.64%
benchmark old MB/s new MB/s speedup
BenchmarkCodeEncoder 40.07 52.48 1.31x
Additionally, the numbers seem stable now at ~52 MB/s, whereas
the numbers for the old code were all over the place: 11 MB/s,
40 MB/s, 13 MB/s, 39 MB/s, etc. In the benchmark above I compared
against the best I saw the old code do.
R=rsc, adg
CC=gobot, golang-dev, r
https://golang.org/cl/9129044
2013-08-09 09:46:47 -07:00
|
|
|
t.Errorf(" In: %q", strings.Map(noSpace, string(in)))
|
|
|
|
|
t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc)))
|
2012-05-29 18:02:40 -04:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-04-21 16:40:53 -07:00
|
|
|
}
|
2009-11-30 13:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
func TestUnmarshalMarshal(t *testing.T) {
|
2011-03-26 11:25:22 -07:00
|
|
|
initBig()
|
2010-04-21 16:40:53 -07:00
|
|
|
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)
|
|
|
|
|
}
|
2013-01-07 10:03:49 +11:00
|
|
|
if !bytes.Equal(jsonBig, b) {
|
2010-04-21 16:40:53 -07:00
|
|
|
t.Errorf("Marshal jsonBig")
|
|
|
|
|
diff(t, b, jsonBig)
|
|
|
|
|
return
|
|
|
|
|
}
|
2009-11-30 13:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2012-06-25 17:36:09 -04:00
|
|
|
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) {
|
|
|
|
|
t.Errorf("Number(%q).Float64() wanted error %q but got: %v", tt.in, tt.floatErr, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-23 11:32:29 -05:00
|
|
|
func TestLargeByteSlice(t *testing.T) {
|
|
|
|
|
s0 := make([]byte, 2000)
|
|
|
|
|
for i := range s0 {
|
|
|
|
|
s0[i] = byte(i)
|
|
|
|
|
}
|
|
|
|
|
b, err := Marshal(s0)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Marshal: %v", err)
|
|
|
|
|
}
|
|
|
|
|
var s1 []byte
|
|
|
|
|
if err := Unmarshal(b, &s1); err != nil {
|
|
|
|
|
t.Fatalf("Unmarshal: %v", err)
|
|
|
|
|
}
|
2013-01-07 10:03:49 +11:00
|
|
|
if !bytes.Equal(s0, s1) {
|
2011-02-23 11:32:29 -05:00
|
|
|
t.Errorf("Marshal large byte slice")
|
|
|
|
|
diff(t, s0, s1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-27 10:24:00 -07:00
|
|
|
type Xint struct {
|
|
|
|
|
X int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestUnmarshalInterface(t *testing.T) {
|
|
|
|
|
var xint Xint
|
|
|
|
|
var i interface{} = &xint
|
|
|
|
|
if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil {
|
|
|
|
|
t.Fatalf("Unmarshal: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if xint.X != 1 {
|
|
|
|
|
t.Fatalf("Did not write to xint")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestUnmarshalPtrPtr(t *testing.T) {
|
|
|
|
|
var xint Xint
|
|
|
|
|
pxint := &xint
|
|
|
|
|
if err := Unmarshal([]byte(`{"X":1}`), &pxint); err != nil {
|
|
|
|
|
t.Fatalf("Unmarshal: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if xint.X != 1 {
|
|
|
|
|
t.Fatalf("Did not write to xint")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-14 13:30:08 +10:00
|
|
|
func TestEscape(t *testing.T) {
|
2013-07-12 14:35:55 +10:00
|
|
|
const input = `"foobar"<html>` + " [\u2028 \u2029]"
|
|
|
|
|
const expected = `"\"foobar\"\u003chtml\u003e [\u2028 \u2029]"`
|
2011-07-14 13:30:08 +10:00
|
|
|
b, err := Marshal(input)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Marshal error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if s := string(b); s != expected {
|
2013-07-12 14:35:55 +10:00
|
|
|
t.Errorf("Encoding of [%s]:\n got [%s]\nwant [%s]", input, s, expected)
|
2011-07-14 13:30:08 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-15 10:02:47 -08:00
|
|
|
// WrongString is a struct that's misusing the ,string modifier.
|
|
|
|
|
type WrongString struct {
|
|
|
|
|
Message string `json:"result,string"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type wrongStringTest struct {
|
|
|
|
|
in, err string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var wrongStringTests = []wrongStringTest{
|
2012-01-12 14:40:29 -08:00
|
|
|
{`{"result":"x"}`, `json: invalid use of ,string struct tag, trying to unmarshal "x" into string`},
|
|
|
|
|
{`{"result":"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "foo" into string`},
|
|
|
|
|
{`{"result":"123"}`, `json: invalid use of ,string struct tag, trying to unmarshal "123" into string`},
|
2014-12-27 20:52:17 +01:00
|
|
|
{`{"result":123}`, `json: invalid use of ,string struct tag, trying to unmarshal unquoted value into string`},
|
2019-08-20 17:29:04 -04:00
|
|
|
{`{"result":"\""}`, `json: invalid use of ,string struct tag, trying to unmarshal "\"" into string`},
|
|
|
|
|
{`{"result":"\"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "\"foo" into string`},
|
2011-12-15 10:02:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If people misuse the ,string modifier, the error message should be
|
|
|
|
|
// helpful, telling the user that they're doing it wrong.
|
|
|
|
|
func TestErrorMessageFromMisusedString(t *testing.T) {
|
|
|
|
|
for n, tt := range wrongStringTests {
|
|
|
|
|
r := strings.NewReader(tt.in)
|
|
|
|
|
var s WrongString
|
|
|
|
|
err := NewDecoder(r).Decode(&s)
|
|
|
|
|
got := fmt.Sprintf("%v", err)
|
|
|
|
|
if got != tt.err {
|
|
|
|
|
t.Errorf("%d. got err = %q, want %q", n, got, tt.err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-25 22:23:54 -07:00
|
|
|
func noSpace(c rune) rune {
|
2015-09-21 00:45:21 +02:00
|
|
|
if isSpace(byte(c)) { //only used for ascii
|
2010-04-21 16:40:53 -07:00
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
return c
|
2009-11-30 13:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
type All struct {
|
|
|
|
|
Bool bool
|
|
|
|
|
Int int
|
|
|
|
|
Int8 int8
|
|
|
|
|
Int16 int16
|
|
|
|
|
Int32 int32
|
|
|
|
|
Int64 int64
|
|
|
|
|
Uint uint
|
|
|
|
|
Uint8 uint8
|
|
|
|
|
Uint16 uint16
|
|
|
|
|
Uint32 uint32
|
|
|
|
|
Uint64 uint64
|
|
|
|
|
Uintptr uintptr
|
|
|
|
|
Float32 float32
|
|
|
|
|
Float64 float64
|
2009-11-30 13:55:09 -08:00
|
|
|
|
2011-08-26 12:27:33 +04:00
|
|
|
Foo string `json:"bar"`
|
|
|
|
|
Foo2 string `json:"bar2,dummyopt"`
|
2009-11-30 13:55:09 -08:00
|
|
|
|
2017-11-08 07:16:07 -08:00
|
|
|
IntStr int64 `json:",string"`
|
|
|
|
|
UintptrStr uintptr `json:",string"`
|
2011-08-29 12:46:32 -07:00
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
PBool *bool
|
|
|
|
|
PInt *int
|
|
|
|
|
PInt8 *int8
|
|
|
|
|
PInt16 *int16
|
|
|
|
|
PInt32 *int32
|
|
|
|
|
PInt64 *int64
|
|
|
|
|
PUint *uint
|
|
|
|
|
PUint8 *uint8
|
|
|
|
|
PUint16 *uint16
|
|
|
|
|
PUint32 *uint32
|
|
|
|
|
PUint64 *uint64
|
|
|
|
|
PUintptr *uintptr
|
|
|
|
|
PFloat32 *float32
|
|
|
|
|
PFloat64 *float64
|
|
|
|
|
|
|
|
|
|
String string
|
|
|
|
|
PString *string
|
2009-11-30 13:55:09 -08:00
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
Map map[string]Small
|
|
|
|
|
MapP map[string]*Small
|
|
|
|
|
PMap *map[string]Small
|
|
|
|
|
PMapP *map[string]*Small
|
|
|
|
|
|
|
|
|
|
EmptyMap map[string]Small
|
|
|
|
|
NilMap map[string]Small
|
|
|
|
|
|
|
|
|
|
Slice []Small
|
|
|
|
|
SliceP []*Small
|
|
|
|
|
PSlice *[]Small
|
|
|
|
|
PSliceP *[]*Small
|
|
|
|
|
|
|
|
|
|
EmptySlice []Small
|
|
|
|
|
NilSlice []Small
|
|
|
|
|
|
|
|
|
|
StringSlice []string
|
|
|
|
|
ByteSlice []byte
|
|
|
|
|
|
|
|
|
|
Small Small
|
|
|
|
|
PSmall *Small
|
|
|
|
|
PPSmall **Small
|
|
|
|
|
|
|
|
|
|
Interface interface{}
|
|
|
|
|
PInterface *interface{}
|
2011-01-12 11:59:33 +11:00
|
|
|
|
|
|
|
|
unexported int
|
2009-11-30 13:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
type Small struct {
|
|
|
|
|
Tag string
|
2009-11-30 13:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
var allValue = All{
|
2017-11-08 07:16:07 -08:00
|
|
|
Bool: true,
|
|
|
|
|
Int: 2,
|
|
|
|
|
Int8: 3,
|
|
|
|
|
Int16: 4,
|
|
|
|
|
Int32: 5,
|
|
|
|
|
Int64: 6,
|
|
|
|
|
Uint: 7,
|
|
|
|
|
Uint8: 8,
|
|
|
|
|
Uint16: 9,
|
|
|
|
|
Uint32: 10,
|
|
|
|
|
Uint64: 11,
|
|
|
|
|
Uintptr: 12,
|
|
|
|
|
Float32: 14.1,
|
|
|
|
|
Float64: 15.1,
|
|
|
|
|
Foo: "foo",
|
|
|
|
|
Foo2: "foo2",
|
|
|
|
|
IntStr: 42,
|
|
|
|
|
UintptrStr: 44,
|
|
|
|
|
String: "16",
|
2010-04-21 16:40:53 -07:00
|
|
|
Map: map[string]Small{
|
2010-10-22 10:06:33 -07:00
|
|
|
"17": {Tag: "tag17"},
|
|
|
|
|
"18": {Tag: "tag18"},
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
|
|
|
|
MapP: map[string]*Small{
|
2011-12-02 14:14:25 -05:00
|
|
|
"19": {Tag: "tag19"},
|
2010-04-21 16:40:53 -07:00
|
|
|
"20": nil,
|
|
|
|
|
},
|
|
|
|
|
EmptyMap: map[string]Small{},
|
2010-10-22 10:06:33 -07:00
|
|
|
Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}},
|
2011-12-02 14:14:25 -05:00
|
|
|
SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}},
|
2010-04-21 16:40:53 -07:00
|
|
|
EmptySlice: []Small{},
|
|
|
|
|
StringSlice: []string{"str24", "str25", "str26"},
|
|
|
|
|
ByteSlice: []byte{27, 28, 29},
|
|
|
|
|
Small: Small{Tag: "tag30"},
|
|
|
|
|
PSmall: &Small{Tag: "tag31"},
|
2011-01-19 23:09:00 -05:00
|
|
|
Interface: 5.2,
|
2009-11-30 13:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
var pallValue = All{
|
|
|
|
|
PBool: &allValue.Bool,
|
|
|
|
|
PInt: &allValue.Int,
|
|
|
|
|
PInt8: &allValue.Int8,
|
|
|
|
|
PInt16: &allValue.Int16,
|
|
|
|
|
PInt32: &allValue.Int32,
|
|
|
|
|
PInt64: &allValue.Int64,
|
|
|
|
|
PUint: &allValue.Uint,
|
|
|
|
|
PUint8: &allValue.Uint8,
|
|
|
|
|
PUint16: &allValue.Uint16,
|
|
|
|
|
PUint32: &allValue.Uint32,
|
|
|
|
|
PUint64: &allValue.Uint64,
|
|
|
|
|
PUintptr: &allValue.Uintptr,
|
|
|
|
|
PFloat32: &allValue.Float32,
|
|
|
|
|
PFloat64: &allValue.Float64,
|
|
|
|
|
PString: &allValue.String,
|
|
|
|
|
PMap: &allValue.Map,
|
|
|
|
|
PMapP: &allValue.MapP,
|
|
|
|
|
PSlice: &allValue.Slice,
|
|
|
|
|
PSliceP: &allValue.SliceP,
|
|
|
|
|
PPSmall: &allValue.PSmall,
|
|
|
|
|
PInterface: &allValue.Interface,
|
2009-11-30 13:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2010-04-21 16:40:53 -07:00
|
|
|
var allValueIndent = `{
|
2010-04-27 10:24:00 -07:00
|
|
|
"Bool": true,
|
|
|
|
|
"Int": 2,
|
|
|
|
|
"Int8": 3,
|
|
|
|
|
"Int16": 4,
|
|
|
|
|
"Int32": 5,
|
|
|
|
|
"Int64": 6,
|
|
|
|
|
"Uint": 7,
|
|
|
|
|
"Uint8": 8,
|
|
|
|
|
"Uint16": 9,
|
|
|
|
|
"Uint32": 10,
|
|
|
|
|
"Uint64": 11,
|
|
|
|
|
"Uintptr": 12,
|
|
|
|
|
"Float32": 14.1,
|
|
|
|
|
"Float64": 15.1,
|
2010-04-21 16:40:53 -07:00
|
|
|
"bar": "foo",
|
2011-08-26 12:27:33 +04:00
|
|
|
"bar2": "foo2",
|
2011-08-29 12:46:32 -07:00
|
|
|
"IntStr": "42",
|
2017-11-08 07:16:07 -08:00
|
|
|
"UintptrStr": "44",
|
2010-04-27 10:24:00 -07:00
|
|
|
"PBool": null,
|
|
|
|
|
"PInt": null,
|
|
|
|
|
"PInt8": null,
|
|
|
|
|
"PInt16": null,
|
|
|
|
|
"PInt32": null,
|
|
|
|
|
"PInt64": null,
|
|
|
|
|
"PUint": null,
|
|
|
|
|
"PUint8": null,
|
|
|
|
|
"PUint16": null,
|
|
|
|
|
"PUint32": null,
|
|
|
|
|
"PUint64": null,
|
|
|
|
|
"PUintptr": null,
|
|
|
|
|
"PFloat32": null,
|
|
|
|
|
"PFloat64": null,
|
|
|
|
|
"String": "16",
|
|
|
|
|
"PString": null,
|
|
|
|
|
"Map": {
|
2010-04-21 16:40:53 -07:00
|
|
|
"17": {
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag17"
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
|
|
|
|
"18": {
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag18"
|
2009-11-30 13:55:09 -08:00
|
|
|
}
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
2010-04-27 10:24:00 -07:00
|
|
|
"MapP": {
|
2010-04-21 16:40:53 -07:00
|
|
|
"19": {
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag19"
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
|
|
|
|
"20": null
|
|
|
|
|
},
|
2010-04-27 10:24:00 -07:00
|
|
|
"PMap": null,
|
|
|
|
|
"PMapP": null,
|
|
|
|
|
"EmptyMap": {},
|
|
|
|
|
"NilMap": null,
|
|
|
|
|
"Slice": [
|
2010-04-21 16:40:53 -07:00
|
|
|
{
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag20"
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
|
|
|
|
{
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag21"
|
2010-04-21 16:40:53 -07:00
|
|
|
}
|
|
|
|
|
],
|
2010-04-27 10:24:00 -07:00
|
|
|
"SliceP": [
|
2010-04-21 16:40:53 -07:00
|
|
|
{
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag22"
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
|
|
|
|
null,
|
|
|
|
|
{
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag23"
|
2010-04-21 16:40:53 -07:00
|
|
|
}
|
|
|
|
|
],
|
2010-04-27 10:24:00 -07:00
|
|
|
"PSlice": null,
|
|
|
|
|
"PSliceP": null,
|
|
|
|
|
"EmptySlice": [],
|
2011-10-31 13:59:23 -04:00
|
|
|
"NilSlice": null,
|
2010-04-27 10:24:00 -07:00
|
|
|
"StringSlice": [
|
2010-04-21 16:40:53 -07:00
|
|
|
"str24",
|
|
|
|
|
"str25",
|
|
|
|
|
"str26"
|
|
|
|
|
],
|
2011-02-23 11:32:29 -05:00
|
|
|
"ByteSlice": "Gxwd",
|
2010-04-27 10:24:00 -07:00
|
|
|
"Small": {
|
|
|
|
|
"Tag": "tag30"
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
2010-04-27 10:24:00 -07:00
|
|
|
"PSmall": {
|
|
|
|
|
"Tag": "tag31"
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
2010-04-27 10:24:00 -07:00
|
|
|
"PPSmall": null,
|
|
|
|
|
"Interface": 5.2,
|
|
|
|
|
"PInterface": null
|
2010-04-21 16:40:53 -07:00
|
|
|
}`
|
|
|
|
|
|
|
|
|
|
var allValueCompact = strings.Map(noSpace, allValueIndent)
|
|
|
|
|
|
|
|
|
|
var pallValueIndent = `{
|
2010-04-27 10:24:00 -07:00
|
|
|
"Bool": false,
|
|
|
|
|
"Int": 0,
|
|
|
|
|
"Int8": 0,
|
|
|
|
|
"Int16": 0,
|
|
|
|
|
"Int32": 0,
|
|
|
|
|
"Int64": 0,
|
|
|
|
|
"Uint": 0,
|
|
|
|
|
"Uint8": 0,
|
|
|
|
|
"Uint16": 0,
|
|
|
|
|
"Uint32": 0,
|
|
|
|
|
"Uint64": 0,
|
|
|
|
|
"Uintptr": 0,
|
|
|
|
|
"Float32": 0,
|
|
|
|
|
"Float64": 0,
|
2010-04-21 16:40:53 -07:00
|
|
|
"bar": "",
|
2011-08-26 12:27:33 +04:00
|
|
|
"bar2": "",
|
2011-08-29 12:46:32 -07:00
|
|
|
"IntStr": "0",
|
2017-11-08 07:16:07 -08:00
|
|
|
"UintptrStr": "0",
|
2010-04-27 10:24:00 -07:00
|
|
|
"PBool": true,
|
|
|
|
|
"PInt": 2,
|
|
|
|
|
"PInt8": 3,
|
|
|
|
|
"PInt16": 4,
|
|
|
|
|
"PInt32": 5,
|
|
|
|
|
"PInt64": 6,
|
|
|
|
|
"PUint": 7,
|
|
|
|
|
"PUint8": 8,
|
|
|
|
|
"PUint16": 9,
|
|
|
|
|
"PUint32": 10,
|
|
|
|
|
"PUint64": 11,
|
|
|
|
|
"PUintptr": 12,
|
|
|
|
|
"PFloat32": 14.1,
|
|
|
|
|
"PFloat64": 15.1,
|
|
|
|
|
"String": "",
|
|
|
|
|
"PString": "16",
|
|
|
|
|
"Map": null,
|
|
|
|
|
"MapP": null,
|
|
|
|
|
"PMap": {
|
2010-04-21 16:40:53 -07:00
|
|
|
"17": {
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag17"
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
|
|
|
|
"18": {
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag18"
|
2010-04-21 16:40:53 -07:00
|
|
|
}
|
|
|
|
|
},
|
2010-04-27 10:24:00 -07:00
|
|
|
"PMapP": {
|
2010-04-21 16:40:53 -07:00
|
|
|
"19": {
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag19"
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
|
|
|
|
"20": null
|
|
|
|
|
},
|
2010-04-27 10:24:00 -07:00
|
|
|
"EmptyMap": null,
|
|
|
|
|
"NilMap": null,
|
2011-10-31 13:59:23 -04:00
|
|
|
"Slice": null,
|
|
|
|
|
"SliceP": null,
|
2010-04-27 10:24:00 -07:00
|
|
|
"PSlice": [
|
2010-04-21 16:40:53 -07:00
|
|
|
{
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag20"
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
|
|
|
|
{
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag21"
|
2010-04-21 16:40:53 -07:00
|
|
|
}
|
|
|
|
|
],
|
2010-04-27 10:24:00 -07:00
|
|
|
"PSliceP": [
|
2010-04-21 16:40:53 -07:00
|
|
|
{
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag22"
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
|
|
|
|
null,
|
|
|
|
|
{
|
2010-04-27 10:24:00 -07:00
|
|
|
"Tag": "tag23"
|
2010-04-21 16:40:53 -07:00
|
|
|
}
|
|
|
|
|
],
|
2011-10-31 13:59:23 -04:00
|
|
|
"EmptySlice": null,
|
|
|
|
|
"NilSlice": null,
|
|
|
|
|
"StringSlice": null,
|
|
|
|
|
"ByteSlice": null,
|
2010-04-27 10:24:00 -07:00
|
|
|
"Small": {
|
|
|
|
|
"Tag": ""
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
2010-04-27 10:24:00 -07:00
|
|
|
"PSmall": null,
|
|
|
|
|
"PPSmall": {
|
|
|
|
|
"Tag": "tag31"
|
2010-04-21 16:40:53 -07:00
|
|
|
},
|
2010-04-27 10:24:00 -07:00
|
|
|
"Interface": null,
|
|
|
|
|
"PInterface": 5.2
|
2010-04-21 16:40:53 -07:00
|
|
|
}`
|
|
|
|
|
|
|
|
|
|
var pallValueCompact = strings.Map(noSpace, pallValueIndent)
|
2012-02-03 11:15:06 +11:00
|
|
|
|
|
|
|
|
func TestRefUnmarshal(t *testing.T) {
|
|
|
|
|
type S struct {
|
|
|
|
|
// Ref is defined in encode_test.go.
|
|
|
|
|
R0 Ref
|
|
|
|
|
R1 *Ref
|
2013-08-14 14:56:07 -04:00
|
|
|
R2 RefText
|
|
|
|
|
R3 *RefText
|
2012-02-03 11:15:06 +11:00
|
|
|
}
|
|
|
|
|
want := S{
|
|
|
|
|
R0: 12,
|
|
|
|
|
R1: new(Ref),
|
2013-08-14 14:56:07 -04:00
|
|
|
R2: 13,
|
|
|
|
|
R3: new(RefText),
|
2012-02-03 11:15:06 +11:00
|
|
|
}
|
|
|
|
|
*want.R1 = 12
|
2013-08-14 14:56:07 -04:00
|
|
|
*want.R3 = 13
|
2012-02-03 11:15:06 +11:00
|
|
|
|
|
|
|
|
var got S
|
2013-08-14 14:56:07 -04:00
|
|
|
if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref","R2":"ref","R3":"ref"}`), &got); err != nil {
|
2012-02-03 11:15:06 +11:00
|
|
|
t.Fatalf("Unmarshal: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(got, want) {
|
|
|
|
|
t.Errorf("got %+v, want %+v", got, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-02-19 00:27:05 -05:00
|
|
|
|
2012-05-03 17:35:44 -04:00
|
|
|
// Test that the empty string doesn't panic decoding when ,string is specified
|
|
|
|
|
// Issue 3450
|
|
|
|
|
func TestEmptyString(t *testing.T) {
|
|
|
|
|
type T2 struct {
|
|
|
|
|
Number1 int `json:",string"`
|
|
|
|
|
Number2 int `json:",string"`
|
|
|
|
|
}
|
|
|
|
|
data := `{"Number1":"1", "Number2":""}`
|
|
|
|
|
dec := NewDecoder(strings.NewReader(data))
|
|
|
|
|
var t2 T2
|
|
|
|
|
err := dec.Decode(&t2)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("Decode: did not return error")
|
|
|
|
|
}
|
|
|
|
|
if t2.Number1 != 1 {
|
|
|
|
|
t.Fatal("Decode: did not set Number1")
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-06-07 01:48:55 -04:00
|
|
|
|
2014-10-07 11:07:04 -04:00
|
|
|
// Test that a null for ,string is not replaced with the previous quoted string (issue 7046).
|
|
|
|
|
// It should also not be an error (issue 2540, issue 8587).
|
2014-01-03 10:13:28 -08:00
|
|
|
func TestNullString(t *testing.T) {
|
|
|
|
|
type T struct {
|
2014-10-07 11:07:04 -04:00
|
|
|
A int `json:",string"`
|
|
|
|
|
B int `json:",string"`
|
|
|
|
|
C *int `json:",string"`
|
2014-01-03 10:13:28 -08:00
|
|
|
}
|
2014-10-07 11:07:04 -04:00
|
|
|
data := []byte(`{"A": "1", "B": null, "C": null}`)
|
2014-01-03 10:13:28 -08:00
|
|
|
var s T
|
2014-10-07 11:07:04 -04:00
|
|
|
s.B = 1
|
|
|
|
|
s.C = new(int)
|
|
|
|
|
*s.C = 2
|
2014-01-03 10:13:28 -08:00
|
|
|
err := Unmarshal(data, &s)
|
2014-10-07 11:07:04 -04:00
|
|
|
if err != nil {
|
2014-12-27 20:52:17 +01:00
|
|
|
t.Fatalf("Unmarshal: %v", err)
|
2014-10-07 11:07:04 -04:00
|
|
|
}
|
|
|
|
|
if s.B != 1 || s.C != nil {
|
|
|
|
|
t.Fatalf("after Unmarshal, s.B=%d, s.C=%p, want 1, nil", s.B, s.C)
|
2014-01-03 10:13:28 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-07 01:48:55 -04:00
|
|
|
func intp(x int) *int {
|
|
|
|
|
p := new(int)
|
|
|
|
|
*p = x
|
|
|
|
|
return p
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func intpp(x *int) **int {
|
|
|
|
|
pp := new(*int)
|
|
|
|
|
*pp = x
|
|
|
|
|
return pp
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var interfaceSetTests = []struct {
|
|
|
|
|
pre interface{}
|
|
|
|
|
json string
|
|
|
|
|
post interface{}
|
|
|
|
|
}{
|
|
|
|
|
{"foo", `"bar"`, "bar"},
|
|
|
|
|
{"foo", `2`, 2.0},
|
|
|
|
|
{"foo", `true`, true},
|
|
|
|
|
{"foo", `null`, nil},
|
|
|
|
|
|
|
|
|
|
{nil, `null`, nil},
|
|
|
|
|
{new(int), `null`, nil},
|
|
|
|
|
{(*int)(nil), `null`, nil},
|
|
|
|
|
{new(*int), `null`, new(*int)},
|
|
|
|
|
{(**int)(nil), `null`, nil},
|
|
|
|
|
{intp(1), `null`, nil},
|
|
|
|
|
{intpp(nil), `null`, intpp(nil)},
|
|
|
|
|
{intpp(intp(1)), `null`, intpp(nil)},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInterfaceSet(t *testing.T) {
|
|
|
|
|
for _, tt := range interfaceSetTests {
|
|
|
|
|
b := struct{ X interface{} }{tt.pre}
|
|
|
|
|
blob := `{"X":` + tt.json + `}`
|
|
|
|
|
if err := Unmarshal([]byte(blob), &b); err != nil {
|
|
|
|
|
t.Errorf("Unmarshal %#q: %v", blob, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(b.X, tt.post) {
|
|
|
|
|
t.Errorf("Unmarshal %#q into %#v: X=%#v, want %#v", blob, tt.pre, b.X, tt.post)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-11-12 15:35:11 -05:00
|
|
|
|
2016-10-12 16:54:02 -04:00
|
|
|
type NullTest struct {
|
|
|
|
|
Bool bool
|
|
|
|
|
Int int
|
|
|
|
|
Int8 int8
|
|
|
|
|
Int16 int16
|
|
|
|
|
Int32 int32
|
|
|
|
|
Int64 int64
|
|
|
|
|
Uint uint
|
|
|
|
|
Uint8 uint8
|
|
|
|
|
Uint16 uint16
|
|
|
|
|
Uint32 uint32
|
|
|
|
|
Uint64 uint64
|
|
|
|
|
Float32 float32
|
|
|
|
|
Float64 float64
|
|
|
|
|
String string
|
|
|
|
|
PBool *bool
|
|
|
|
|
Map map[string]string
|
|
|
|
|
Slice []string
|
|
|
|
|
Interface interface{}
|
|
|
|
|
|
|
|
|
|
PRaw *RawMessage
|
|
|
|
|
PTime *time.Time
|
|
|
|
|
PBigInt *big.Int
|
|
|
|
|
PText *MustNotUnmarshalText
|
|
|
|
|
PBuffer *bytes.Buffer // has methods, just not relevant ones
|
|
|
|
|
PStruct *struct{}
|
|
|
|
|
|
|
|
|
|
Raw RawMessage
|
|
|
|
|
Time time.Time
|
|
|
|
|
BigInt big.Int
|
|
|
|
|
Text MustNotUnmarshalText
|
|
|
|
|
Buffer bytes.Buffer
|
|
|
|
|
Struct struct{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type NullTestStrings struct {
|
|
|
|
|
Bool bool `json:",string"`
|
|
|
|
|
Int int `json:",string"`
|
|
|
|
|
Int8 int8 `json:",string"`
|
|
|
|
|
Int16 int16 `json:",string"`
|
|
|
|
|
Int32 int32 `json:",string"`
|
|
|
|
|
Int64 int64 `json:",string"`
|
|
|
|
|
Uint uint `json:",string"`
|
|
|
|
|
Uint8 uint8 `json:",string"`
|
|
|
|
|
Uint16 uint16 `json:",string"`
|
|
|
|
|
Uint32 uint32 `json:",string"`
|
|
|
|
|
Uint64 uint64 `json:",string"`
|
|
|
|
|
Float32 float32 `json:",string"`
|
|
|
|
|
Float64 float64 `json:",string"`
|
|
|
|
|
String string `json:",string"`
|
|
|
|
|
PBool *bool `json:",string"`
|
|
|
|
|
Map map[string]string `json:",string"`
|
|
|
|
|
Slice []string `json:",string"`
|
|
|
|
|
Interface interface{} `json:",string"`
|
|
|
|
|
|
|
|
|
|
PRaw *RawMessage `json:",string"`
|
|
|
|
|
PTime *time.Time `json:",string"`
|
|
|
|
|
PBigInt *big.Int `json:",string"`
|
|
|
|
|
PText *MustNotUnmarshalText `json:",string"`
|
|
|
|
|
PBuffer *bytes.Buffer `json:",string"`
|
|
|
|
|
PStruct *struct{} `json:",string"`
|
|
|
|
|
|
|
|
|
|
Raw RawMessage `json:",string"`
|
|
|
|
|
Time time.Time `json:",string"`
|
|
|
|
|
BigInt big.Int `json:",string"`
|
|
|
|
|
Text MustNotUnmarshalText `json:",string"`
|
|
|
|
|
Buffer bytes.Buffer `json:",string"`
|
|
|
|
|
Struct struct{} `json:",string"`
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-12 15:35:11 -05:00
|
|
|
// JSON null values should be ignored for primitives and string values instead of resulting in an error.
|
|
|
|
|
// Issue 2540
|
|
|
|
|
func TestUnmarshalNulls(t *testing.T) {
|
2016-10-12 16:54:02 -04:00
|
|
|
// Unmarshal docs:
|
|
|
|
|
// The JSON null value unmarshals into an interface, map, pointer, or slice
|
|
|
|
|
// by setting that Go value to nil. Because null is often used in JSON to mean
|
|
|
|
|
// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
|
|
|
|
|
// on the value and produces no error.
|
|
|
|
|
|
2012-11-12 15:35:11 -05:00
|
|
|
jsonData := []byte(`{
|
2016-10-12 16:54:02 -04:00
|
|
|
"Bool" : null,
|
|
|
|
|
"Int" : null,
|
|
|
|
|
"Int8" : null,
|
|
|
|
|
"Int16" : null,
|
|
|
|
|
"Int32" : null,
|
|
|
|
|
"Int64" : null,
|
|
|
|
|
"Uint" : null,
|
|
|
|
|
"Uint8" : null,
|
|
|
|
|
"Uint16" : null,
|
|
|
|
|
"Uint32" : null,
|
|
|
|
|
"Uint64" : null,
|
|
|
|
|
"Float32" : null,
|
|
|
|
|
"Float64" : null,
|
|
|
|
|
"String" : null,
|
|
|
|
|
"PBool": null,
|
|
|
|
|
"Map": null,
|
|
|
|
|
"Slice": null,
|
|
|
|
|
"Interface": null,
|
|
|
|
|
"PRaw": null,
|
|
|
|
|
"PTime": null,
|
|
|
|
|
"PBigInt": null,
|
|
|
|
|
"PText": null,
|
|
|
|
|
"PBuffer": null,
|
|
|
|
|
"PStruct": null,
|
|
|
|
|
"Raw": null,
|
|
|
|
|
"Time": null,
|
|
|
|
|
"BigInt": null,
|
|
|
|
|
"Text": null,
|
|
|
|
|
"Buffer": null,
|
|
|
|
|
"Struct": null
|
|
|
|
|
}`)
|
|
|
|
|
nulls := NullTest{
|
|
|
|
|
Bool: true,
|
|
|
|
|
Int: 2,
|
|
|
|
|
Int8: 3,
|
|
|
|
|
Int16: 4,
|
|
|
|
|
Int32: 5,
|
|
|
|
|
Int64: 6,
|
|
|
|
|
Uint: 7,
|
|
|
|
|
Uint8: 8,
|
|
|
|
|
Uint16: 9,
|
|
|
|
|
Uint32: 10,
|
|
|
|
|
Uint64: 11,
|
|
|
|
|
Float32: 12.1,
|
|
|
|
|
Float64: 13.1,
|
|
|
|
|
String: "14",
|
|
|
|
|
PBool: new(bool),
|
|
|
|
|
Map: map[string]string{},
|
|
|
|
|
Slice: []string{},
|
|
|
|
|
Interface: new(MustNotUnmarshalJSON),
|
|
|
|
|
PRaw: new(RawMessage),
|
|
|
|
|
PTime: new(time.Time),
|
|
|
|
|
PBigInt: new(big.Int),
|
|
|
|
|
PText: new(MustNotUnmarshalText),
|
|
|
|
|
PStruct: new(struct{}),
|
|
|
|
|
PBuffer: new(bytes.Buffer),
|
|
|
|
|
Raw: RawMessage("123"),
|
|
|
|
|
Time: time.Unix(123456789, 0),
|
|
|
|
|
BigInt: *big.NewInt(123),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
before := nulls.Time.String()
|
2012-11-12 15:35:11 -05:00
|
|
|
|
|
|
|
|
err := Unmarshal(jsonData, &nulls)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Unmarshal of null values failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 ||
|
|
|
|
|
nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 ||
|
|
|
|
|
nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" {
|
|
|
|
|
t.Errorf("Unmarshal of null values affected primitives")
|
|
|
|
|
}
|
2016-10-12 16:54:02 -04:00
|
|
|
|
|
|
|
|
if nulls.PBool != nil {
|
|
|
|
|
t.Errorf("Unmarshal of null did not clear nulls.PBool")
|
|
|
|
|
}
|
|
|
|
|
if nulls.Map != nil {
|
|
|
|
|
t.Errorf("Unmarshal of null did not clear nulls.Map")
|
|
|
|
|
}
|
|
|
|
|
if nulls.Slice != nil {
|
|
|
|
|
t.Errorf("Unmarshal of null did not clear nulls.Slice")
|
|
|
|
|
}
|
|
|
|
|
if nulls.Interface != nil {
|
|
|
|
|
t.Errorf("Unmarshal of null did not clear nulls.Interface")
|
|
|
|
|
}
|
|
|
|
|
if nulls.PRaw != nil {
|
|
|
|
|
t.Errorf("Unmarshal of null did not clear nulls.PRaw")
|
|
|
|
|
}
|
|
|
|
|
if nulls.PTime != nil {
|
|
|
|
|
t.Errorf("Unmarshal of null did not clear nulls.PTime")
|
|
|
|
|
}
|
|
|
|
|
if nulls.PBigInt != nil {
|
|
|
|
|
t.Errorf("Unmarshal of null did not clear nulls.PBigInt")
|
|
|
|
|
}
|
|
|
|
|
if nulls.PText != nil {
|
|
|
|
|
t.Errorf("Unmarshal of null did not clear nulls.PText")
|
|
|
|
|
}
|
|
|
|
|
if nulls.PBuffer != nil {
|
|
|
|
|
t.Errorf("Unmarshal of null did not clear nulls.PBuffer")
|
|
|
|
|
}
|
|
|
|
|
if nulls.PStruct != nil {
|
|
|
|
|
t.Errorf("Unmarshal of null did not clear nulls.PStruct")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if string(nulls.Raw) != "null" {
|
|
|
|
|
t.Errorf("Unmarshal of RawMessage null did not record null: %v", string(nulls.Raw))
|
|
|
|
|
}
|
|
|
|
|
if nulls.Time.String() != before {
|
|
|
|
|
t.Errorf("Unmarshal of time.Time null set time to %v", nulls.Time.String())
|
|
|
|
|
}
|
|
|
|
|
if nulls.BigInt.String() != "123" {
|
|
|
|
|
t.Errorf("Unmarshal of big.Int null set int to %v", nulls.BigInt.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type MustNotUnmarshalJSON struct{}
|
|
|
|
|
|
|
|
|
|
func (x MustNotUnmarshalJSON) UnmarshalJSON(data []byte) error {
|
|
|
|
|
return errors.New("MustNotUnmarshalJSON was used")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type MustNotUnmarshalText struct{}
|
|
|
|
|
|
|
|
|
|
func (x MustNotUnmarshalText) UnmarshalText(text []byte) error {
|
|
|
|
|
return errors.New("MustNotUnmarshalText was used")
|
2012-11-12 15:35:11 -05:00
|
|
|
}
|
2012-12-30 15:40:42 +11:00
|
|
|
|
|
|
|
|
func TestStringKind(t *testing.T) {
|
|
|
|
|
type stringKind string
|
|
|
|
|
|
|
|
|
|
var m1, m2 map[stringKind]int
|
|
|
|
|
m1 = map[stringKind]int{
|
|
|
|
|
"foo": 42,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, err := Marshal(m1)
|
|
|
|
|
if err != nil {
|
2015-09-01 17:51:39 +10:00
|
|
|
t.Errorf("Unexpected error marshaling: %v", err)
|
2012-12-30 15:40:42 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = Unmarshal(data, &m2)
|
|
|
|
|
if err != nil {
|
2015-09-01 17:51:39 +10:00
|
|
|
t.Errorf("Unexpected error unmarshaling: %v", err)
|
2012-12-30 15:40:42 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(m1, m2) {
|
|
|
|
|
t.Error("Items should be equal after encoding and then decoding")
|
|
|
|
|
}
|
2015-04-26 23:52:42 +02:00
|
|
|
}
|
|
|
|
|
|
2016-11-09 14:49:12 -08:00
|
|
|
// Custom types with []byte as underlying type could not be marshaled
|
|
|
|
|
// and then unmarshaled.
|
2015-04-26 23:52:42 +02:00
|
|
|
// Issue 8962.
|
|
|
|
|
func TestByteKind(t *testing.T) {
|
|
|
|
|
type byteKind []byte
|
|
|
|
|
|
|
|
|
|
a := byteKind("hello")
|
2012-12-30 15:40:42 +11:00
|
|
|
|
2015-04-26 23:52:42 +02:00
|
|
|
data, err := Marshal(a)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error(err)
|
|
|
|
|
}
|
|
|
|
|
var b byteKind
|
|
|
|
|
err = Unmarshal(data, &b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(a, b) {
|
|
|
|
|
t.Errorf("expected %v == %v", a, b)
|
|
|
|
|
}
|
2012-12-30 15:40:42 +11:00
|
|
|
}
|
2013-01-14 08:44:16 +01:00
|
|
|
|
2015-10-25 22:42:41 +01:00
|
|
|
// The fix for issue 8962 introduced a regression.
|
|
|
|
|
// Issue 12921.
|
|
|
|
|
func TestSliceOfCustomByte(t *testing.T) {
|
|
|
|
|
type Uint8 uint8
|
|
|
|
|
|
|
|
|
|
a := []Uint8("hello")
|
|
|
|
|
|
|
|
|
|
data, err := Marshal(a)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
var b []Uint8
|
|
|
|
|
err = Unmarshal(data, &b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(a, b) {
|
2016-03-22 06:37:16 +01:00
|
|
|
t.Fatalf("expected %v == %v", a, b)
|
2015-10-25 22:42:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-14 08:44:16 +01:00
|
|
|
var decodeTypeErrorTests = []struct {
|
|
|
|
|
dest interface{}
|
|
|
|
|
src string
|
|
|
|
|
}{
|
|
|
|
|
{new(string), `{"user": "name"}`}, // issue 4628.
|
|
|
|
|
{new(error), `{}`}, // issue 4222
|
|
|
|
|
{new(error), `[]`},
|
|
|
|
|
{new(error), `""`},
|
|
|
|
|
{new(error), `123`},
|
|
|
|
|
{new(error), `true`},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestUnmarshalTypeError(t *testing.T) {
|
|
|
|
|
for _, item := range decodeTypeErrorTests {
|
|
|
|
|
err := Unmarshal([]byte(item.src), item.dest)
|
|
|
|
|
if _, ok := err.(*UnmarshalTypeError); !ok {
|
2013-01-29 13:34:18 -08:00
|
|
|
t.Errorf("expected type error for Unmarshal(%q, type %T): got %T",
|
2013-01-14 08:44:16 +01:00
|
|
|
item.src, item.dest, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-01-22 17:49:07 -05:00
|
|
|
|
2013-01-29 13:34:18 -08:00
|
|
|
var unmarshalSyntaxTests = []string{
|
|
|
|
|
"tru",
|
|
|
|
|
"fals",
|
|
|
|
|
"nul",
|
|
|
|
|
"123e",
|
|
|
|
|
`"hello`,
|
|
|
|
|
`[1,2,3`,
|
|
|
|
|
`{"key":1`,
|
|
|
|
|
`{"key":1,`,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestUnmarshalSyntax(t *testing.T) {
|
|
|
|
|
var x interface{}
|
|
|
|
|
for _, src := range unmarshalSyntaxTests {
|
|
|
|
|
err := Unmarshal([]byte(src), &x)
|
|
|
|
|
if _, ok := err.(*SyntaxError); !ok {
|
|
|
|
|
t.Errorf("expected syntax error for Unmarshal(%q): got %T", src, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-22 17:49:07 -05:00
|
|
|
// Test handling of unexported fields that should be ignored.
|
|
|
|
|
// Issue 4660
|
|
|
|
|
type unexportedFields struct {
|
|
|
|
|
Name string
|
|
|
|
|
m map[string]interface{} `json:"-"`
|
|
|
|
|
m2 map[string]interface{} `json:"abcd"`
|
2018-09-11 22:09:00 +02:00
|
|
|
|
|
|
|
|
s []int `json:"-"`
|
2013-01-22 17:49:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestUnmarshalUnexported(t *testing.T) {
|
2018-09-11 22:09:00 +02:00
|
|
|
input := `{"Name": "Bob", "m": {"x": 123}, "m2": {"y": 456}, "abcd": {"z": 789}, "s": [2, 3]}`
|
2013-01-22 17:49:07 -05:00
|
|
|
want := &unexportedFields{Name: "Bob"}
|
|
|
|
|
|
|
|
|
|
out := &unexportedFields{}
|
|
|
|
|
err := Unmarshal([]byte(input), out)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("got error %v, expected nil", err)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(out, want) {
|
|
|
|
|
t.Errorf("got %q, want %q", out, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-14 14:46:15 -05:00
|
|
|
|
|
|
|
|
// 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] != '"' {
|
2013-02-25 12:43:03 -08:00
|
|
|
return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b)
|
2013-02-14 14:46:15 -05:00
|
|
|
}
|
|
|
|
|
tm, err := time.Parse(time.RFC3339, string(b[1:len(b)-1]))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
*t = Time3339(tm)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-30 14:05:04 +00:00
|
|
|
func TestUnmarshalJSONLiteralError(t *testing.T) {
|
2013-02-14 14:46:15 -05:00
|
|
|
var t3 Time3339
|
|
|
|
|
err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatalf("expected error; got time %v", time.Time(t3))
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(err.Error(), "range") {
|
|
|
|
|
t.Errorf("got err = %v; want out of range error", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-03-13 14:53:03 -04:00
|
|
|
|
|
|
|
|
// 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]interface{}
|
|
|
|
|
|
|
|
|
|
err := Unmarshal([]byte(json), &dest)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("got error %q, want nil", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-09-09 19:11:05 -04:00
|
|
|
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
// Test semantics of pre-filled data, such as struct fields, map elements,
|
|
|
|
|
// slices, and arrays.
|
|
|
|
|
// Issues 4900 and 8837, among others.
|
2013-09-09 19:11:05 -04:00
|
|
|
func TestPrefilled(t *testing.T) {
|
|
|
|
|
// Values here change, cannot reuse table across runs.
|
|
|
|
|
var prefillTests = []struct {
|
|
|
|
|
in string
|
|
|
|
|
ptr interface{}
|
|
|
|
|
out interface{}
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
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},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `{"X": 1, "Y": 2}`,
|
encoding/json: fix the broken "overwriting of data" tests
Because TestUnmarshal actually allocates a new value to decode into
using ptr's pointer type, any existing data is thrown away. This was
harmless in alomst all of the test cases, minus the "overwriting of
data" ones added in 2015 in CL 12209.
I spotted that nothing covered decoding a JSON array with few elements
into a slice which already had many elements. I initially assumed that
the code was buggy or that some code could be removed, when in fact
there simply wasn't any code covering the edge case.
Move those two tests to TestPrefilled, which already served a very
similar purpose. Remove the map case, as TestPrefilled already has
plenty of prefilled map cases. Moreover, we no longer reset an entire
map when decoding, as per the godoc:
To unmarshal a JSON object into a map, Unmarshal first
establishes a map to use. If the map is nil, Unmarshal allocates
a new map. Otherwise Unmarshal reuses the existing map, keeping
existing entries.
Finally, to ensure that ptr is used correctly in the future, make
TestUnmarshal error if it's anything other than a pointer to a zero
value. That is, the only correct use should be new(type). Don't rename
the ptr field, as that would be extremely noisy and cause unwanted merge
conflicts.
Change-Id: I41e3ecfeae42d877ac5443a6bd622ac3d6c8120c
Reviewed-on: https://go-review.googlesource.com/c/go/+/185738
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
2019-07-11 21:27:19 +09:00
|
|
|
ptr: &map[string]interface{}{"X": float32(3), "Y": int16(4), "Z": 1.5},
|
|
|
|
|
out: &map[string]interface{}{"X": float64(1), "Y": float64(2), "Z": 1.5},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `[2]`,
|
|
|
|
|
ptr: &[]int{1},
|
|
|
|
|
out: &[]int{2},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `[2, 3]`,
|
|
|
|
|
ptr: &[]int{1},
|
|
|
|
|
out: &[]int{2, 3},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `[2, 3]`,
|
|
|
|
|
ptr: &[...]int{1},
|
|
|
|
|
out: &[...]int{2},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `[3]`,
|
|
|
|
|
ptr: &[...]int{1, 2},
|
|
|
|
|
out: &[...]int{3, 0},
|
2013-09-09 19:11:05 -04:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range prefillTests {
|
|
|
|
|
ptrstr := fmt.Sprintf("%v", tt.ptr)
|
|
|
|
|
err := Unmarshal([]byte(tt.in), tt.ptr) // tt.ptr edited here
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Unmarshal: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(tt.ptr, tt.out) {
|
|
|
|
|
t.Errorf("Unmarshal(%#q, %s): have %v, want %v", tt.in, ptrstr, tt.ptr, tt.out)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-01-02 09:49:55 +11:00
|
|
|
|
|
|
|
|
var invalidUnmarshalTests = []struct {
|
|
|
|
|
v interface{}
|
|
|
|
|
want string
|
|
|
|
|
}{
|
|
|
|
|
{nil, "json: Unmarshal(nil)"},
|
|
|
|
|
{struct{}{}, "json: Unmarshal(non-pointer struct {})"},
|
|
|
|
|
{(*int)(nil), "json: Unmarshal(nil *int)"},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInvalidUnmarshal(t *testing.T) {
|
|
|
|
|
buf := []byte(`{"a":"1"}`)
|
|
|
|
|
for _, tt := range invalidUnmarshalTests {
|
|
|
|
|
err := Unmarshal(buf, tt.v)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Errorf("Unmarshal expecting error, got nil")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if got := err.Error(); got != tt.want {
|
|
|
|
|
t.Errorf("Unmarshal = %q; want %q", got, tt.want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-16 23:01:39 -04:00
|
|
|
|
2015-07-14 19:31:44 -04:00
|
|
|
var invalidUnmarshalTextTests = []struct {
|
|
|
|
|
v interface{}
|
|
|
|
|
want string
|
|
|
|
|
}{
|
|
|
|
|
{nil, "json: Unmarshal(nil)"},
|
|
|
|
|
{struct{}{}, "json: Unmarshal(non-pointer struct {})"},
|
|
|
|
|
{(*int)(nil), "json: Unmarshal(nil *int)"},
|
2016-10-12 16:54:02 -04:00
|
|
|
{new(net.IP), "json: cannot unmarshal number into Go value of type *net.IP"},
|
2015-07-14 19:31:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInvalidUnmarshalText(t *testing.T) {
|
|
|
|
|
buf := []byte(`123`)
|
|
|
|
|
for _, tt := range invalidUnmarshalTextTests {
|
|
|
|
|
err := Unmarshal(buf, tt.v)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Errorf("Unmarshal expecting error, got nil")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if got := err.Error(); got != tt.want {
|
|
|
|
|
t.Errorf("Unmarshal = %q; want %q", got, tt.want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-16 23:01:39 -04:00
|
|
|
// 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 interface{} `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: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = Unmarshal(data, &item)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Unmarshal: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-05 22:38:36 -08:00
|
|
|
|
2018-02-28 13:45:06 -08:00
|
|
|
// 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.
|
2018-10-11 14:43:47 +01:00
|
|
|
//
|
|
|
|
|
// (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.
|
2018-02-28 13:45:06 -08:00
|
|
|
func TestUnmarshalEmbeddedUnexported(t *testing.T) {
|
2017-12-05 22:38:36 -08:00
|
|
|
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
|
|
|
|
|
}
|
2018-02-28 13:45:06 -08:00
|
|
|
S6 struct {
|
|
|
|
|
embed1 `json:"embed1"`
|
|
|
|
|
}
|
|
|
|
|
S7 struct {
|
|
|
|
|
embed1 `json:"embed1"`
|
|
|
|
|
embed2
|
|
|
|
|
}
|
|
|
|
|
S8 struct {
|
|
|
|
|
embed1 `json:"embed1"`
|
|
|
|
|
embed2 `json:"embed2"`
|
|
|
|
|
Q int
|
|
|
|
|
}
|
2018-10-11 14:43:47 +01:00
|
|
|
S9 struct {
|
|
|
|
|
unexportedWithMethods `json:"embed"`
|
|
|
|
|
}
|
2017-12-05 22:38:36 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
in string
|
|
|
|
|
ptr interface{}
|
|
|
|
|
out interface{}
|
|
|
|
|
err error
|
|
|
|
|
}{{
|
|
|
|
|
// Error since we cannot set S1.embed1, but still able to set S1.R.
|
|
|
|
|
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.
|
|
|
|
|
in: `{"Q":1}`,
|
|
|
|
|
ptr: new(S2),
|
|
|
|
|
out: &S2{Q: 1},
|
|
|
|
|
}, {
|
|
|
|
|
// No issue with non-pointer variant.
|
|
|
|
|
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.
|
|
|
|
|
in: `{"R":2}`,
|
|
|
|
|
ptr: new(S4),
|
|
|
|
|
out: new(S4),
|
|
|
|
|
}, {
|
|
|
|
|
// Error since we cannot set S5.embed1, but still able to set S5.R.
|
|
|
|
|
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"),
|
2018-02-28 13:45:06 -08:00
|
|
|
}, {
|
|
|
|
|
// Issue 24152, ensure decodeState.indirect does not panic.
|
|
|
|
|
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.
|
|
|
|
|
in: `{"embed1": {"Q": 1}, "Q": 2}`,
|
|
|
|
|
ptr: new(S7),
|
|
|
|
|
out: &S7{embed1{1}, embed2{2}},
|
|
|
|
|
}, {
|
|
|
|
|
// Issue 24153, similar to the S7 case.
|
|
|
|
|
in: `{"embed1": {"Q": 1}, "embed2": {"Q": 2}, "Q": 3}`,
|
|
|
|
|
ptr: new(S8),
|
|
|
|
|
out: &S8{embed1{1}, embed2{2}, 3},
|
2018-10-11 14:43:47 +01:00
|
|
|
}, {
|
|
|
|
|
// Issue 228145, similar to the cases above.
|
|
|
|
|
in: `{"embed": {}}`,
|
|
|
|
|
ptr: new(S9),
|
|
|
|
|
out: &S9{},
|
2017-12-05 22:38:36 -08:00
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
for i, tt := range tests {
|
|
|
|
|
err := Unmarshal([]byte(tt.in), tt.ptr)
|
2019-02-08 17:52:08 +01:00
|
|
|
if !equalError(err, tt.err) {
|
2017-12-05 22:38:36 -08:00
|
|
|
t.Errorf("#%d: %v, want %v", i, err, tt.err)
|
|
|
|
|
}
|
|
|
|
|
if !reflect.DeepEqual(tt.ptr, tt.out) {
|
|
|
|
|
t.Errorf("#%d: mismatch\ngot: %#+v\nwant: %#+v", i, tt.ptr, tt.out)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-05 22:53:48 -08:00
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
}
|
2019-04-29 22:57:25 +07:00
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
|
var v interface{}
|
|
|
|
|
v = &v
|
|
|
|
|
data := []byte(`{"a": "b"}`)
|
|
|
|
|
|
|
|
|
|
if err := Unmarshal(data, v); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
}
|