1
0
mirror of https://github.com/golang/go synced 2024-10-05 00:21:21 -06:00
go/src/pkg/encoding/json/decode_test.go
Russ Cox 990f9f4c00 encoding/json: disable anonymous fields
We should, after Go 1, make them work the same as
package xml, that is, make them appear in the outer
struct.  For now turn them off so that people do not
depend on the old behavior.

Fixing them is issue 3069.

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5656102
2012-02-19 00:27:05 -05:00

651 lines
13 KiB
Go

// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package json
import (
"bytes"
"fmt"
"reflect"
"strings"
"testing"
)
type T struct {
X string
Y int
Z int `json:"-"`
}
type tx struct {
x int
}
var txType = reflect.TypeOf((*tx)(nil)).Elem()
// A type that can unmarshal itself.
type unmarshaler struct {
T bool
}
func (u *unmarshaler) UnmarshalJSON(b []byte) error {
*u = unmarshaler{true} // All we need to see that UnmarshalJson is called.
return nil
}
type ustruct struct {
M unmarshaler
}
var (
um0, um1 unmarshaler // target2 of unmarshaling
ump = &um1
umtrue = unmarshaler{true}
umslice = []unmarshaler{{true}}
umslicep = new([]unmarshaler)
umstruct = ustruct{unmarshaler{true}}
)
type unmarshalTest struct {
in string
ptr interface{}
out interface{}
err error
}
var unmarshalTests = []unmarshalTest{
// basic types
{`true`, new(bool), true, nil},
{`1`, new(int), 1, nil},
{`1.2`, new(float64), 1.2, nil},
{`-5`, new(int16), int16(-5), nil},
{`"a\u1234"`, new(string), "a\u1234", nil},
{`"http:\/\/"`, new(string), "http://", nil},
{`"g-clef: \uD834\uDD1E"`, new(string), "g-clef: \U0001D11E", nil},
{`"invalid: \uD834x\uDD1E"`, new(string), "invalid: \uFFFDx\uFFFD", nil},
{"null", new(interface{}), nil, nil},
{`{"X": [1,2,3], "Y": 4}`, new(T), T{Y: 4}, &UnmarshalTypeError{"array", reflect.TypeOf("")}},
{`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}},
// Z has a "-" tag.
{`{"Y": 1, "Z": 2}`, new(T), T{Y: 1}, nil},
// syntax errors
{`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}},
{`[1, 2, 3+]`, nil, nil, &SyntaxError{"invalid character '+' after array element", 9}},
// array tests
{`[1, 2, 3]`, new([3]int), [3]int{1, 2, 3}, nil},
{`[1, 2, 3]`, new([1]int), [1]int{1}, nil},
{`[1, 2, 3]`, new([5]int), [5]int{1, 2, 3, 0, 0}, nil},
// composite tests
{allValueIndent, new(All), allValue, nil},
{allValueCompact, new(All), allValue, nil},
{allValueIndent, new(*All), &allValue, nil},
{allValueCompact, new(*All), &allValue, nil},
{pallValueIndent, new(All), pallValue, nil},
{pallValueCompact, new(All), pallValue, nil},
{pallValueIndent, new(*All), &pallValue, nil},
{pallValueCompact, new(*All), &pallValue, nil},
// unmarshal interface test
{`{"T":false}`, &um0, umtrue, nil}, // use "false" so test will fail if custom unmarshaler is not called
{`{"T":false}`, &ump, &umtrue, nil},
{`[{"T":false}]`, &umslice, umslice, nil},
{`[{"T":false}]`, &umslicep, &umslice, nil},
{`{"M":{"T":false}}`, &umstruct, umstruct, nil},
}
func TestMarshal(t *testing.T) {
b, err := Marshal(allValue)
if err != nil {
t.Fatalf("Marshal allValue: %v", err)
}
if string(b) != allValueCompact {
t.Errorf("Marshal allValueCompact")
diff(t, b, []byte(allValueCompact))
return
}
b, err = Marshal(pallValue)
if err != nil {
t.Fatalf("Marshal pallValue: %v", err)
}
if string(b) != pallValueCompact {
t.Errorf("Marshal pallValueCompact")
diff(t, b, []byte(pallValueCompact))
return
}
}
func TestMarshalBadUTF8(t *testing.T) {
s := "hello\xffworld"
b, err := Marshal(s)
if err == nil {
t.Fatal("Marshal bad UTF8: no error")
}
if len(b) != 0 {
t.Fatal("Marshal returned data")
}
if _, ok := err.(*InvalidUTF8Error); !ok {
t.Fatalf("Marshal did not return InvalidUTF8Error: %T %v", err, err)
}
}
func TestUnmarshal(t *testing.T) {
for i, tt := range unmarshalTests {
var scan scanner
in := []byte(tt.in)
if err := checkValid(in, &scan); err != nil {
if !reflect.DeepEqual(err, tt.err) {
t.Errorf("#%d: checkValid: %#v", i, err)
continue
}
}
if tt.ptr == nil {
continue
}
// v = new(right-type)
v := reflect.New(reflect.TypeOf(tt.ptr).Elem())
if err := Unmarshal([]byte(in), v.Interface()); !reflect.DeepEqual(err, tt.err) {
t.Errorf("#%d: %v want %v", i, err, tt.err)
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
}
}
}
func TestUnmarshalMarshal(t *testing.T) {
initBig()
var v interface{}
if err := Unmarshal(jsonBig, &v); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
b, err := Marshal(v)
if err != nil {
t.Fatalf("Marshal: %v", err)
}
if bytes.Compare(jsonBig, b) != 0 {
t.Errorf("Marshal jsonBig")
diff(t, b, jsonBig)
return
}
}
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)
}
if bytes.Compare(s0, s1) != 0 {
t.Errorf("Marshal large byte slice")
diff(t, s0, s1)
}
}
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")
}
}
func TestEscape(t *testing.T) {
const input = `"foobar"<html>`
const expected = `"\"foobar\"\u003chtml\u003e"`
b, err := Marshal(input)
if err != nil {
t.Fatalf("Marshal error: %v", err)
}
if s := string(b); s != expected {
t.Errorf("Encoding of [%s] was [%s], want [%s]", input, s, expected)
}
}
func TestHTMLEscape(t *testing.T) {
b, err := MarshalForHTML("foobarbaz<>&quux")
if err != nil {
t.Fatalf("MarshalForHTML error: %v", err)
}
if !bytes.Equal(b, []byte(`"foobarbaz\u003c\u003e\u0026quux"`)) {
t.Fatalf("Unexpected encoding of \"<>&\": %s", b)
}
}
// 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{
{`{"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`},
}
// 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)
}
}
}
func noSpace(c rune) rune {
if isSpace(c) {
return -1
}
return c
}
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
Foo string `json:"bar"`
Foo2 string `json:"bar2,dummyopt"`
IntStr int64 `json:",string"`
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
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{}
unexported int
}
type Small struct {
Tag string
}
var allValue = All{
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,
String: "16",
Map: map[string]Small{
"17": {Tag: "tag17"},
"18": {Tag: "tag18"},
},
MapP: map[string]*Small{
"19": {Tag: "tag19"},
"20": nil,
},
EmptyMap: map[string]Small{},
Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}},
SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}},
EmptySlice: []Small{},
StringSlice: []string{"str24", "str25", "str26"},
ByteSlice: []byte{27, 28, 29},
Small: Small{Tag: "tag30"},
PSmall: &Small{Tag: "tag31"},
Interface: 5.2,
}
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,
}
var allValueIndent = `{
"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,
"bar": "foo",
"bar2": "foo2",
"IntStr": "42",
"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": {
"17": {
"Tag": "tag17"
},
"18": {
"Tag": "tag18"
}
},
"MapP": {
"19": {
"Tag": "tag19"
},
"20": null
},
"PMap": null,
"PMapP": null,
"EmptyMap": {},
"NilMap": null,
"Slice": [
{
"Tag": "tag20"
},
{
"Tag": "tag21"
}
],
"SliceP": [
{
"Tag": "tag22"
},
null,
{
"Tag": "tag23"
}
],
"PSlice": null,
"PSliceP": null,
"EmptySlice": [],
"NilSlice": null,
"StringSlice": [
"str24",
"str25",
"str26"
],
"ByteSlice": "Gxwd",
"Small": {
"Tag": "tag30"
},
"PSmall": {
"Tag": "tag31"
},
"PPSmall": null,
"Interface": 5.2,
"PInterface": null
}`
var allValueCompact = strings.Map(noSpace, allValueIndent)
var pallValueIndent = `{
"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,
"bar": "",
"bar2": "",
"IntStr": "0",
"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": {
"17": {
"Tag": "tag17"
},
"18": {
"Tag": "tag18"
}
},
"PMapP": {
"19": {
"Tag": "tag19"
},
"20": null
},
"EmptyMap": null,
"NilMap": null,
"Slice": null,
"SliceP": null,
"PSlice": [
{
"Tag": "tag20"
},
{
"Tag": "tag21"
}
],
"PSliceP": [
{
"Tag": "tag22"
},
null,
{
"Tag": "tag23"
}
],
"EmptySlice": null,
"NilSlice": null,
"StringSlice": null,
"ByteSlice": null,
"Small": {
"Tag": ""
},
"PSmall": null,
"PPSmall": {
"Tag": "tag31"
},
"Interface": null,
"PInterface": 5.2
}`
var pallValueCompact = strings.Map(noSpace, pallValueIndent)
func TestRefUnmarshal(t *testing.T) {
type S struct {
// Ref is defined in encode_test.go.
R0 Ref
R1 *Ref
}
want := S{
R0: 12,
R1: new(Ref),
}
*want.R1 = 12
var got S
if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref"}`), &got); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %+v, want %+v", got, want)
}
}
// Test that anonymous fields are ignored.
// We may assign meaning to them later.
func TestAnonymous(t *testing.T) {
type S struct {
T
N int
}
data, err := Marshal(new(S))
if err != nil {
t.Fatalf("Marshal: %v", err)
}
want := `{"N":0}`
if string(data) != want {
t.Fatalf("Marshal = %#q, want %#q", string(data), want)
}
var s S
if err := Unmarshal([]byte(`{"T": 1, "T": {"Y": 1}, "N": 2}`), &s); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if s.N != 2 {
t.Fatal("Unmarshal: did not set N")
}
if s.T.Y != 0 {
t.Fatal("Unmarshal: did set T.Y")
}
}