Skip to content

Commit

Permalink
Merge pull request #70 from francoispqt/version/1.2.4
Browse files Browse the repository at this point in the history
Version/1.2.4
  • Loading branch information
francoispqt committed Aug 29, 2018
2 parents 0d1a893 + 7b5e7ac commit f558782
Show file tree
Hide file tree
Showing 19 changed files with 967 additions and 6 deletions.
60 changes: 58 additions & 2 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,11 +526,21 @@ func (dec *Decoder) AddObject(v UnmarshalerJSONObject) error {
return dec.Object(v)
}

// AddObjectNull decodes the next key to a UnmarshalerJSONObject.
func (dec *Decoder) AddObjectNull(v interface{}) error {
return dec.ObjectNull(v)
}

// AddArray decodes the next key to a UnmarshalerJSONArray.
func (dec *Decoder) AddArray(v UnmarshalerJSONArray) error {
return dec.Array(v)
}

// AddArray decodes the next key to a UnmarshalerJSONArray.
func (dec *Decoder) AddArrayNull(v UnmarshalerJSONArray) error {
return dec.ArrayNull(v)
}

// AddInterface decodes the next key to a interface{}.
func (dec *Decoder) AddInterface(v *interface{}) error {
return dec.Interface(v)
Expand Down Expand Up @@ -870,9 +880,44 @@ func (dec *Decoder) Object(value UnmarshalerJSONObject) error {
return nil
}

// ObjectNull decodes the next key to a UnmarshalerJSONObject.
// v should be a pointer to an UnmarshalerJSONObject,
// if `null` value is encountered in JSON, it will leave the value v untouched,
// else it will create a new instance of the UnmarshalerJSONObject behind v.
func (dec *Decoder) ObjectNull(v interface{}) error {
initialKeysDone := dec.keysDone
initialChild := dec.child
dec.keysDone = 0
dec.called = 0
dec.child |= 1
newCursor, err := dec.decodeObjectNull(v)
if err != nil {
return err
}
dec.cursor = newCursor
dec.keysDone = initialKeysDone
dec.child = initialChild
dec.called |= 1
return nil
}

// Array decodes the next key to a UnmarshalerJSONArray.
func (dec *Decoder) Array(value UnmarshalerJSONArray) error {
newCursor, err := dec.decodeArray(value)
func (dec *Decoder) Array(v UnmarshalerJSONArray) error {
newCursor, err := dec.decodeArray(v)
if err != nil {
return err
}
dec.cursor = newCursor
dec.called |= 1
return nil
}

// ArrayNull decodes the next key to a UnmarshalerJSONArray.
// v should be a pointer to an UnmarshalerJSONArray,
// if `null` value is encountered in JSON, it will leave the value v untouched,
// else it will create a new instance of the UnmarshalerJSONArray behind v.
func (dec *Decoder) ArrayNull(v interface{}) error {
newCursor, err := dec.decodeArrayNull(v)
if err != nil {
return err
}
Expand All @@ -891,6 +936,17 @@ func (dec *Decoder) Interface(value *interface{}) error {
return nil
}

// Array decodes the next key to a UnmarshalerJSONArray.
// func (dec *Decoder) ArrayNull(factory func() UnmarshalerJSONArray) error {
// newCursor, err := dec.decodeArrayNull(factory)
// if err != nil {
// return err
// }
// dec.cursor = newCursor
// dec.called |= 1
// return nil
// }

// Non exported

func isDigit(b byte) bool {
Expand Down
67 changes: 64 additions & 3 deletions decode_array.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package gojay

import "reflect"

// DecodeArray reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
//
// v must implement UnmarshalerJSONArray.
Expand All @@ -13,19 +15,79 @@ func (dec *Decoder) DecodeArray(arr UnmarshalerJSONArray) error {
return err
}
func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
switch dec.data[dec.cursor] {
case ' ', '\n', '\t', '\r', ',':
continue
case '[':
dec.cursor = dec.cursor + 1
// array is open, char is not space start readings
for dec.nextChar() != 0 {
// closing array
if dec.data[dec.cursor] == ']' {
dec.cursor = dec.cursor + 1
return dec.cursor, nil
}
// calling unmarshall function for each element of the slice
err := arr.UnmarshalJSONArray(dec)
if err != nil {
return 0, err
}
}
return 0, dec.raiseInvalidJSONErr(dec.cursor)
case 'n':
// is null
dec.cursor++
err := dec.assertNull()
if err != nil {
return 0, err
}
dec.cursor++
return dec.cursor, nil
case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
// can't unmarshall to struct
// we skip array and set Error
dec.err = dec.makeInvalidUnmarshalErr(arr)
err := dec.skipData()
if err != nil {
return 0, err
}
return dec.cursor, nil
default:
return 0, dec.raiseInvalidJSONErr(dec.cursor)
}
}
return 0, dec.raiseInvalidJSONErr(dec.cursor)
}
func (dec *Decoder) decodeArrayNull(v interface{}) (int, error) {
vv := reflect.ValueOf(v)
vvt := vv.Type()
if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr {
dec.err = ErrUnmarshalPtrExpected
return 0, dec.err
}
// not an array not an error, but do not know what to do
// do not check syntax
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
switch dec.data[dec.cursor] {
case ' ', '\n', '\t', '\r', ',':
continue
case '[':
n := 0
dec.cursor = dec.cursor + 1
// create our new type
elt := vv.Elem()
n := reflect.New(elt.Type().Elem())
var arr UnmarshalerJSONArray
var ok bool
if arr, ok = n.Interface().(UnmarshalerJSONArray); !ok {
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil))
return 0, dec.err
}
// array is open, char is not space start readings
for dec.nextChar() != 0 {
// closing array
if dec.data[dec.cursor] == ']' {
elt.Set(n)
dec.cursor = dec.cursor + 1
return dec.cursor, nil
}
Expand All @@ -34,7 +96,6 @@ func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
if err != nil {
return 0, err
}
n++
}
return 0, dec.raiseInvalidJSONErr(dec.cursor)
case 'n':
Expand All @@ -49,7 +110,7 @@ func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
// can't unmarshall to struct
// we skip array and set Error
dec.err = dec.makeInvalidUnmarshalErr(arr)
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil))
err := dec.skipData()
if err != nil {
return 0, err
Expand Down
43 changes: 43 additions & 0 deletions decode_array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,49 @@ func TestSliceObjects(t *testing.T) {
}
}

type ArrayNull []string

func (a *ArrayNull) UnmarshalJSONArray(dec *Decoder) error {
var str string
if err := dec.String(&str); err != nil {
return err
}
*a = append(*a, str)
return nil
}

type ObjectArrayNull struct {
SubArray *ArrayNull
}

func (o *ObjectArrayNull) UnmarshalJSONObject(dec *Decoder, k string) error {
switch k {
case "subarray":
return dec.ArrayNull(&o.SubArray)
}
return nil
}

func (o *ObjectArrayNull) NKeys() int {
return 1
}

func TestDecodeArrayNullPtr(t *testing.T) {
t.Run("sub obj should not be nil", func(t *testing.T) {
var o = &ObjectArrayNull{}
var err = UnmarshalJSONObject([]byte(`{"subarray": ["test"]}`), o)
assert.Nil(t, err)
assert.NotNil(t, o.SubArray)
assert.Len(t, *o.SubArray, 1)
})
t.Run("sub array should be nil", func(t *testing.T) {
var o = &ObjectArrayNull{}
var err = UnmarshalJSONObject([]byte(`{"subarray": null}`), o)
assert.Nil(t, err)
assert.Nil(t, o.SubArray)
})
}

type testChannelArray chan *TestObj

func (c *testChannelArray) UnmarshalJSONArray(dec *Decoder) error {
Expand Down
101 changes: 101 additions & 0 deletions decode_object.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gojay

import (
"reflect"
"unsafe"
)

Expand Down Expand Up @@ -100,6 +101,106 @@ func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) {
return 0, dec.raiseInvalidJSONErr(dec.cursor)
}

func (dec *Decoder) decodeObjectNull(v interface{}) (int, error) {
// make sure the value is a pointer
vv := reflect.ValueOf(v)
vvt := vv.Type()
if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr {
dec.err = ErrUnmarshalPtrExpected
return 0, dec.err
}
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
switch dec.data[dec.cursor] {
case ' ', '\n', '\t', '\r', ',':
case '{':
elt := vv.Elem()
n := reflect.New(elt.Type().Elem())
elt.Set(n)
var j UnmarshalerJSONObject
var ok bool
if j, ok = n.Interface().(UnmarshalerJSONObject); !ok {
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil))
return 0, dec.err
}
keys := j.NKeys()
dec.cursor = dec.cursor + 1
// if keys is zero we will parse all keys
// we run two loops for micro optimization
if keys == 0 {
for dec.cursor < dec.length || dec.read() {
k, done, err := dec.nextKey()
if err != nil {
return 0, err
} else if done {
return dec.cursor, nil
}
err = j.UnmarshalJSONObject(dec, k)
if err != nil {
dec.err = err
return 0, err
} else if dec.called&1 == 0 {
err := dec.skipData()
if err != nil {
return 0, err
}
} else {
dec.keysDone++
}
dec.called &= 0
}
} else {
for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys {
k, done, err := dec.nextKey()
if err != nil {
return 0, err
} else if done {
return dec.cursor, nil
}
err = j.UnmarshalJSONObject(dec, k)
if err != nil {
dec.err = err
return 0, err
} else if dec.called&1 == 0 {
err := dec.skipData()
if err != nil {
return 0, err
}
} else {
dec.keysDone++
}
dec.called &= 0
}
}
// will get to that point when keysDone is not lower than keys anymore
// in that case, we make sure cursor goes to the end of object, but we skip
// unmarshalling
if dec.child&1 != 0 {
end, err := dec.skipObject()
dec.cursor = end
return dec.cursor, err
}
return dec.cursor, nil
case 'n':
dec.cursor++
err := dec.assertNull()
if err != nil {
return 0, err
}
dec.cursor++
return dec.cursor, nil
default:
// can't unmarshal to struct
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil))
err := dec.skipData()
if err != nil {
return 0, err
}
return dec.cursor, nil
}
}
return 0, dec.raiseInvalidJSONErr(dec.cursor)
}

func (dec *Decoder) skipObject() (int, error) {
var objectsOpen = 1
var objectsClosed = 0
Expand Down
Loading

0 comments on commit f558782

Please sign in to comment.