gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit 0c6779450769d47ded18c7c53a88e2889ed97bc0
parent c35963f96584327d30ee89005766d166d4b50626
Author: francoispqt <francois@parquet.ninja>
Date:   Sat, 19 May 2018 22:11:41 +0800

add tests

Diffstat:
Mdecode_object.go | 15+++++++--------
Mdecode_object_test.go | 605+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mdecode_pool.go | 7++++---
Mdecode_pool_test.go | 5+++++
Mdecode_stream_pool.go | 7++++---
Mdecode_stream_pool_test.go | 5+++++
Mdecode_string.go | 2+-
Mdecode_string_test.go | 6++++++
Mgojay_test.go | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 708 insertions(+), 91 deletions(-)

diff --git a/decode_object.go b/decode_object.go @@ -97,14 +97,13 @@ func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) { return dec.cursor, nil } } - return 0, dec.raiseInvalidJSONErr(dec.length - 1) + return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) skipObject() (int, error) { var objectsOpen = 1 var objectsClosed = 0 - // var stringOpen byte = 0 - for j := dec.cursor; j < dec.length; j++ { + for j := dec.cursor; j < dec.length || dec.read(); j++ { switch dec.data[j] { case '}': objectsClosed++ @@ -117,7 +116,7 @@ func (dec *Decoder) skipObject() (int, error) { objectsOpen++ case '"': j++ - for ; j < dec.length; j++ { + for ; j < dec.length || dec.read(); j++ { if dec.data[j] != '"' { continue } @@ -127,7 +126,7 @@ func (dec *Decoder) skipObject() (int, error) { // loop backward and count how many anti slash found // to see if string is effectively escaped ct := 1 - for i := j; i > 0; i-- { + for i := j - 1; i > 0; i-- { if dec.data[i] != '\\' { break } @@ -135,14 +134,14 @@ func (dec *Decoder) skipObject() (int, error) { } // is pair number of slashes, quote is not escaped if ct&1 == 0 { - break + continue } } default: continue } } - return 0, nil + return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) nextKey() (string, bool, error) { @@ -177,7 +176,7 @@ func (dec *Decoder) nextKey() (string, bool, error) { return "", false, dec.raiseInvalidJSONErr(dec.cursor) } } - return "", false, dec.raiseInvalidJSONErr(dec.length - 1) + return "", false, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) skipData() error { diff --git a/decode_object_test.go b/decode_object_test.go @@ -1,6 +1,7 @@ package gojay import ( + "fmt" "io" "strings" "testing" @@ -52,6 +53,34 @@ func TestDecodeObjectBasic(t *testing.T) { err: false, }, { + name: "basic-err-invalid-type", + json: `1`, + expectedResult: testObject{}, + err: true, + errType: InvalidUnmarshalError(""), + }, + { + name: "basic-err-invalid-json", + json: `hello`, + expectedResult: testObject{}, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-err-invalid-json", + json: `nall`, + expectedResult: testObject{}, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-err-invalid-type", + json: ``, + expectedResult: testObject{}, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-err", json: `{ "testStr": "hello world!", @@ -271,6 +300,48 @@ func TestDecodeObjectBasic(t *testing.T) { expectedResult: testObject{}, err: true, }, + { + name: "basic-skip-data", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "skipObject": { + "escapedString": "string with escaped \\n new line" + }, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "skipArray": [[],[],{}], + "testUint16": 3455, + "skipBool": true, + "skipNull": null, + "testUint32": 343443, + "testUint64": 545665757, + "skipString": "skipping string with escaped \\n new line", + "skipInt": 3, + }`, + expectedResult: testObject{ + testStr: "hello world!", + testInt: 4535, + testBool: true, + testFloat32: 2.345, + testFloat64: 123.677, + testInt8: 23, + testInt16: 1245, + testInt32: 456778, + testInt64: 1446685358, + testUint8: 255, + testUint16: 3455, + testUint32: 343443, + testUint64: 545665757, + }, + err: false, + }, } for _, testCase := range testCases { @@ -295,6 +366,363 @@ func TestDecodeObjectBasic(t *testing.T) { } } +func TestDecodeObjectBasic0Keys(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult testObject0Keys + err bool + errType interface{} + skipCheckResult bool + }{ + { + name: "basic", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject0Keys{ + testStr: "hello world!", + testInt: 4535, + testBool: true, + testFloat32: 2.345, + testFloat64: 123.677, + testInt8: 23, + testInt16: 1245, + testInt32: 456778, + testInt64: 1446685358, + testUint8: 255, + testUint16: 3455, + testUint32: 343443, + testUint64: 545665757, + }, + err: false, + }, + { + name: "basic-err-invalid-type", + json: `1`, + expectedResult: testObject0Keys{}, + err: true, + errType: InvalidUnmarshalError(""), + }, + { + name: "basic-err-invalid-json", + json: `hello`, + expectedResult: testObject0Keys{}, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-err-invalid-json", + json: `nall`, + expectedResult: testObject0Keys{}, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-err-invalid-type", + json: ``, + expectedResult: testObject0Keys{}, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-err", + json: `{ + "testStr": "hello world!", + "testInt": 453q5, + "testBool": trae, + "testFloat32": 2q.345, + "testFloat64": 12x3.677, + "testInt8": 2s3, + "testInt16": 1245, + "testInt32": 4567q78, + "testInt64": 14466e85358, + "testUint8": 2s55, + "testUint16": 345i5, + "testUint32": 343q443, + "testUint64": 5456657z57 + }`, + expectedResult: testObject0Keys{}, + err: true, + }, + { + name: "basic-err2", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 4567x78, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject0Keys{}, + err: true, + }, + { + name: "basic-err-float32", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2q.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject0Keys{}, + err: true, + }, + { + name: "basic-err-float64", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 1x23.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject0Keys{}, + err: true, + }, + { + name: "basic-err3", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 2q3, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject0Keys{}, + err: true, + }, + { + name: "basic-err-int16", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1x245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject0Keys{}, + err: true, + }, + { + name: "basic-err-int64", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446q685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject0Keys{}, + err: true, + }, + { + name: "basic-err-uint8", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 2x55, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject0Keys{}, + err: true, + }, + { + name: "basic-err-uint16", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3x455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject0Keys{}, + err: true, + }, + { + name: "basic-err-uint32", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 3x43443, + "testUint64": 545665757 + }`, + expectedResult: testObject0Keys{}, + err: true, + }, + { + name: "basic-err-uint64", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 5456x65757 + }`, + expectedResult: testObject0Keys{}, + err: true, + }, + { + name: "basic-skip-data", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "skipObject": { + "escapedString": "string with escaped \\n new line" + }, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "skipArray": [[],[],{}], + "testUint16": 3455, + "skipBool": true, + "skipNull": null, + "testUint32": 343443, + "testUint64": 545665757, + "skipString": "skipping string with escaped \\n new line", + "skipInt": 3, + }`, + expectedResult: testObject0Keys{ + testStr: "hello world!", + testInt: 4535, + testBool: true, + testFloat32: 2.345, + testFloat64: 123.677, + testInt8: 23, + testInt16: 1245, + testInt32: 456778, + testInt64: 1446685358, + testUint8: 255, + testUint16: 3455, + testUint32: 343443, + testUint64: 545665757, + }, + err: false, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + s := testObject0Keys{} + dec := BorrowDecoder(strings.NewReader(testCase.json)) + defer dec.Release() + err := dec.Decode(&s) + if testCase.err { + t.Log(err) + assert.NotNil(t, err, "err should not be nil") + if testCase.errType != nil { + assert.IsType(t, testCase.errType, err, "err should be of the given type") + } + return + } + assert.Nil(t, err, "err should be nil") + if !testCase.skipCheckResult { + assert.Equal(t, testCase.expectedResult, s, "value at given index should be the same as expected results") + } + }) + } +} + func TestDecodeObjectComplex(t *testing.T) { testCases := []struct { name string @@ -306,7 +734,10 @@ func TestDecodeObjectComplex(t *testing.T) { }{ { name: "basic", - json: `{"testSubObject":{},"testSubSliceInts":[1,2]}`, + json: `{ + "testSubObject": {}, + "testSubSliceInts": [1,2] + }`, expectedResult: testObjectComplex{ testSubObject: &testObject{}, testSubSliceInts: &testSliceInts{1, 2}, @@ -315,7 +746,18 @@ func TestDecodeObjectComplex(t *testing.T) { }, { name: "complex", - json: `{"testSubObject":{"testStr":"some string","testInt":124465,"testUint16":120, "testUint8":15,"testInt16":-135,"testInt8":-23},"testSubSliceInts":[1,2],"testStr":"some \\n string"}`, + json: `{ + "testSubObject": { + "testStr": "some string", + "testInt":124465, + "testUint16":120, + "testUint8":15, + "testInt16":-135, + "testInt8":-23 + }, + "testSubSliceInts": [1,2,3,4,5], + "testStr": "some \\n string" + }`, expectedResult: testObjectComplex{ testSubObject: &testObject{ testStr: "some string", @@ -325,7 +767,7 @@ func TestDecodeObjectComplex(t *testing.T) { testInt16: -135, testInt8: -23, }, - testSubSliceInts: &testSliceInts{1, 2}, + testSubSliceInts: &testSliceInts{1, 2, 3, 4, 5}, testStr: "some \n string", }, err: false, @@ -365,75 +807,6 @@ func TestDecodeObjectComplex(t *testing.T) { } } -type TestObj struct { - test int - test2 int - test3 string - test4 string - test5 float64 - testArr testSliceObjects - testSubObj *TestSubObj - testSubObj2 *TestSubObj -} - -type TestSubObj struct { - test3 int - test4 int - test5 string - testSubSubObj *TestSubObj - testSubSubObj2 *TestSubObj -} - -func (t *TestSubObj) UnmarshalJSONObject(dec *Decoder, key string) error { - switch key { - case "test": - return dec.AddInt(&t.test3) - case "test2": - return dec.AddInt(&t.test4) - case "test3": - return dec.AddString(&t.test5) - case "testSubSubObj": - t.testSubSubObj = &TestSubObj{} - return dec.AddObject(t.testSubSubObj) - case "testSubSubObj2": - t.testSubSubObj2 = &TestSubObj{} - return dec.AddObject(t.testSubSubObj2) - } - return nil -} - -func (t *TestSubObj) NKeys() int { - return 0 -} - -func (t *TestObj) UnmarshalJSONObject(dec *Decoder, key string) error { - switch key { - case "test": - return dec.AddInt(&t.test) - case "test2": - return dec.AddInt(&t.test2) - case "test3": - return dec.AddString(&t.test3) - case "test4": - return dec.AddString(&t.test4) - case "test5": - return dec.AddFloat(&t.test5) - case "testSubObj": - t.testSubObj = &TestSubObj{} - return dec.AddObject(t.testSubObj) - case "testSubObj2": - t.testSubObj2 = &TestSubObj{} - return dec.AddObject(t.testSubObj2) - case "testArr": - return dec.AddArray(&t.testArr) - } - return nil -} - -func (t *TestObj) NKeys() int { - return 8 -} - func assertResult(t *testing.T, v *TestObj, err error) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, 245, v.test, "v.test must be equal to 245") @@ -442,10 +815,6 @@ func assertResult(t *testing.T, v *TestObj, err error) { assert.Equal(t, "complex string with spaces and some slashes\"", v.test4, "v.test4 must be equal to 'string'") assert.Equal(t, -1.15657654376543, v.test5, "v.test5 must be equal to 1.15") assert.Len(t, v.testArr, 2, "v.testArr must be of len 2") - // assert.Equal(t, v.testArr[0].test, 245, "v.testArr[0].test must be equal to 245") - // assert.Equal(t, v.testArr[0].test2, 246, "v.testArr[0].test must be equal to 246") - // assert.Equal(t, v.testArr[1].test, 245, "v.testArr[0].test must be equal to 245") - // assert.Equal(t, v.testArr[1].test2, 246, "v.testArr[0].test must be equal to 246") assert.Equal(t, 121, v.testSubObj.test3, "v.testSubObj.test3 must be equal to 121") assert.Equal(t, 122, v.testSubObj.test4, "v.testSubObj.test4 must be equal to 122") @@ -816,6 +1185,90 @@ func TestDecoderObjectPoolError(t *testing.T) { assert.True(t, false, "should not be called as decoder should have panicked") } +func TestNextKey(t *testing.T) { + testCases := []struct { + name string + json string + expectedValue string + err bool + }{ + { + name: "basic", + json: `"key":"value"`, + expectedValue: "key", + }, + { + name: "basic-err", + json: ``, + expectedValue: "", + err: true, + }, + { + name: "basic-err2", + json: `"key"`, + expectedValue: "", + err: true, + }, + { + name: "basic-err3", + json: `"key`, + expectedValue: "", + err: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + dec := BorrowDecoder(strings.NewReader(testCase.json)) + s, _, err := dec.nextKey() + if testCase.err { + assert.NotNil(t, err, "err should not be nil") + return + } + assert.Nil(t, err, "err should be nil") + assert.Equal(t, testCase.expectedValue, s, fmt.Sprintf("s should be '%s'", testCase.expectedValue)) + }) + } +} + +func TestSkipObject(t *testing.T) { + testCases := []struct { + name string + json string + err bool + }{ + { + name: "basic", + json: `"key":"value"}`, + }, + { + name: "basic-escaped", + json: `"key":"value\\\\\\" hello"}`, + }, + { + name: "basic-err", + json: ``, + err: true, + }, + { + name: "basic-err2", + json: `{"key":"value"`, + err: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + dec := BorrowDecoder(strings.NewReader(testCase.json)) + defer dec.Release() + _, err := dec.skipObject() + if testCase.err { + assert.NotNil(t, err, "err should not be nil") + return + } + assert.Nil(t, err, "err should be nil") + }) + } +} + func TestSkipData(t *testing.T) { testCases := []struct { name string diff --git a/decode_pool.go b/decode_pool.go @@ -6,9 +6,7 @@ import ( ) var decPool = sync.Pool{ - New: func() interface{} { - return NewDecoder(nil) - }, + New: newDecoderPool, } func init() { @@ -31,6 +29,9 @@ func NewDecoder(r io.Reader) *Decoder { isPooled: 0, } } +func newDecoderPool() interface{} { + return NewDecoder(nil) +} // BorrowDecoder borrows a Decoder from the pool. // It takes an io.Reader implementation as data input. diff --git a/decode_pool_test.go b/decode_pool_test.go @@ -10,3 +10,8 @@ func TestDecoderBorrowFromPoolSetBuffSize(t *testing.T) { dec := borrowDecoder(nil, 512) assert.Len(t, dec.data, 512, "data buffer should be of len 512") } + +func TestDecoderNewPool(t *testing.T) { + dec := newDecoderPool() + assert.IsType(t, &Decoder{}, dec, "dec should be a *Decoder") +} diff --git a/decode_stream_pool.go b/decode_stream_pool.go @@ -6,9 +6,7 @@ import ( ) var streamDecPool = sync.Pool{ - New: func() interface{} { - return Stream.NewDecoder(nil) - }, + New: newStreamDecoderPool, } // NewDecoder returns a new StreamDecoder. @@ -22,6 +20,9 @@ func (s stream) NewDecoder(r io.Reader) *StreamDecoder { } return streamDec } +func newStreamDecoderPool() interface{} { + return Stream.NewDecoder(nil) +} // BorrowDecoder borrows a StreamDecoder from the pool. // It takes an io.Reader implementation as data input. diff --git a/decode_stream_pool_test.go b/decode_stream_pool_test.go @@ -51,3 +51,8 @@ func TestDecodeStreamDecodePooledDecoderError2(t *testing.T) { // make sure they are the same assert.True(t, false, "should not be called as decoder should have panicked") } + +func TestStreamDecoderNewPool(t *testing.T) { + dec := newStreamDecoderPool() + assert.IsType(t, &StreamDecoder{}, dec, "dec should be a *StreamDecoder") +} diff --git a/decode_string.go b/decode_string.go @@ -172,7 +172,7 @@ func (dec *Decoder) parseEscapedString() error { } } } - return nil + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getString() (int, int, error) { diff --git a/decode_string_test.go b/decode_string_test.go @@ -97,6 +97,12 @@ func TestDecoderString(t *testing.T) { }, { name: "escape-control-char", + json: `"\`, + expectedResult: "", + err: true, + }, + { + name: "escape-control-char", json: `"\\r"`, expectedResult: "\r", err: false, diff --git a/gojay_test.go b/gojay_test.go @@ -78,6 +78,84 @@ func (t *testObject) NKeys() int { return 13 } +type testObject0Keys struct { + testStr string + testInt int + testInt64 int64 + testInt32 int32 + testInt16 int16 + testInt8 int8 + testUint64 uint64 + testUint32 uint32 + testUint16 uint16 + testUint8 uint8 + testFloat64 float64 + testFloat32 float32 + testBool bool + testSubObject *testObject0Keys + testSubArray testSliceInts +} + +// make sure it implements interfaces +var _ MarshalerJSONObject = &testObject0Keys{} +var _ UnmarshalerJSONObject = &testObject0Keys{} + +func (t *testObject0Keys) IsNil() bool { + return t == nil +} + +func (t *testObject0Keys) MarshalJSONObject(enc *Encoder) { + enc.AddStringKey("testStr", t.testStr) + enc.AddIntKey("testInt", t.testInt) + enc.AddIntKey("testInt64", int(t.testInt64)) + enc.AddIntKey("testInt32", int(t.testInt32)) + enc.AddIntKey("testInt16", int(t.testInt16)) + enc.AddIntKey("testInt8", int(t.testInt8)) + enc.AddIntKey("testUint64", int(t.testUint64)) + enc.AddIntKey("testUint32", int(t.testUint32)) + enc.AddIntKey("testUint16", int(t.testUint16)) + enc.AddIntKey("testUint8", int(t.testUint8)) + enc.AddFloatKey("testFloat64", t.testFloat64) + enc.AddFloat32Key("testFloat32", t.testFloat32) + enc.AddBoolKey("testBool", t.testBool) +} + +func (t *testObject0Keys) UnmarshalJSONObject(dec *Decoder, k string) error { + switch k { + case "testStr": + return dec.AddString(&t.testStr) + case "testInt": + return dec.AddInt(&t.testInt) + case "testInt64": + return dec.AddInt64(&t.testInt64) + case "testInt32": + return dec.AddInt32(&t.testInt32) + case "testInt16": + return dec.AddInt16(&t.testInt16) + case "testInt8": + return dec.AddInt8(&t.testInt8) + case "testUint64": + return dec.AddUint64(&t.testUint64) + case "testUint32": + return dec.AddUint32(&t.testUint32) + case "testUint16": + return dec.AddUint16(&t.testUint16) + case "testUint8": + return dec.AddUint8(&t.testUint8) + case "testFloat64": + return dec.AddFloat(&t.testFloat64) + case "testFloat32": + return dec.AddFloat32(&t.testFloat32) + case "testBool": + return dec.AddBool(&t.testBool) + } + return nil +} + +func (t *testObject0Keys) NKeys() int { + return 0 +} + type testObjectComplex struct { testSubObject *testObject testSubSliceInts *testSliceInts @@ -116,3 +194,72 @@ func (t *testObjectComplex) NKeys() int { // make sure it implements interfaces var _ MarshalerJSONObject = &testObjectComplex{} var _ UnmarshalerJSONObject = &testObjectComplex{} + +type TestObj struct { + test int + test2 int + test3 string + test4 string + test5 float64 + testArr testSliceObjects + testSubObj *TestSubObj + testSubObj2 *TestSubObj +} + +type TestSubObj struct { + test3 int + test4 int + test5 string + testSubSubObj *TestSubObj + testSubSubObj2 *TestSubObj +} + +func (t *TestSubObj) UnmarshalJSONObject(dec *Decoder, key string) error { + switch key { + case "test": + return dec.AddInt(&t.test3) + case "test2": + return dec.AddInt(&t.test4) + case "test3": + return dec.AddString(&t.test5) + case "testSubSubObj": + t.testSubSubObj = &TestSubObj{} + return dec.AddObject(t.testSubSubObj) + case "testSubSubObj2": + t.testSubSubObj2 = &TestSubObj{} + return dec.AddObject(t.testSubSubObj2) + } + return nil +} + +func (t *TestSubObj) NKeys() int { + return 0 +} + +func (t *TestObj) UnmarshalJSONObject(dec *Decoder, key string) error { + switch key { + case "test": + return dec.AddInt(&t.test) + case "test2": + return dec.AddInt(&t.test2) + case "test3": + return dec.AddString(&t.test3) + case "test4": + return dec.AddString(&t.test4) + case "test5": + return dec.AddFloat(&t.test5) + case "testSubObj": + t.testSubObj = &TestSubObj{} + return dec.AddObject(t.testSubObj) + case "testSubObj2": + t.testSubObj2 = &TestSubObj{} + return dec.AddObject(t.testSubObj2) + case "testArr": + return dec.AddArray(&t.testArr) + } + return nil +} + +func (t *TestObj) NKeys() int { + return 8 +}