Newer
Older
// 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 (
// 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
}
var (
um0, um1 unmarshaler // target2 of unmarshaling
ump = &um1
umtrue = unmarshaler{true}
umslicep = new([]unmarshaler)
umstruct = ustruct{unmarshaler{true}}
type unmarshalTest struct {
in string
ptr interface{}
out interface{}
var unmarshalTests = []unmarshalTest{
// basic types
{`true`, new(bool), true, nil},
{`1`, new(int), 1, 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},
{`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}},
{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 {
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)
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) {
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)
}
}
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
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)
}
}
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
// WrongString is a struct that's misusing the ,string modifier.
type WrongString struct {
Message string `json:"result,string"`
}
type wrongStringTest struct {
in, err string
}
// TODO(bradfitz): as part of Issue 2331, fix these tests' expected
// error values to be helpful, rather than the confusing messages they
// are now.
var wrongStringTests = []wrongStringTest{
{`{"result":"x"}`, "JSON decoder out of sync - data changing underfoot?"},
{`{"result":"foo"}`, "json: cannot unmarshal bool into Go value of type string"},
{`{"result":"123"}`, "json: cannot unmarshal number into Go value of type 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)
}
}
}
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{}
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",
String: "16",
Map: map[string]Small{
"20": nil,
},
EmptyMap: map[string]Small{},
EmptySlice: []Small{},
StringSlice: []string{"str24", "str25", "str26"},
ByteSlice: []byte{27, 28, 29},
Small: Small{Tag: "tag30"},
PSmall: &Small{Tag: "tag31"},
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,
"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,
"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": {
"PMap": null,
"PMapP": null,
"EmptyMap": {},
"NilMap": null,
"Slice": [
"PSlice": null,
"PSliceP": null,
"EmptySlice": [],
"str24",
"str25",
"str26"
],
"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,
"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": {
"EmptyMap": null,
"NilMap": null,
"EmptySlice": null,
"NilSlice": null,
"StringSlice": null,
"ByteSlice": null,
"PSmall": null,
"PPSmall": {
"Tag": "tag31"
"Interface": null,
"PInterface": 5.2
}`
var pallValueCompact = strings.Map(noSpace, pallValueIndent)