Skip to content

Commit f558782

Browse files
authored
Merge pull request #70 from francoispqt/version/1.2.4
Version/1.2.4
2 parents 0d1a893 + 7b5e7ac commit f558782

19 files changed

+967
-6
lines changed

decode.go

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -526,11 +526,21 @@ func (dec *Decoder) AddObject(v UnmarshalerJSONObject) error {
526526
return dec.Object(v)
527527
}
528528

529+
// AddObjectNull decodes the next key to a UnmarshalerJSONObject.
530+
func (dec *Decoder) AddObjectNull(v interface{}) error {
531+
return dec.ObjectNull(v)
532+
}
533+
529534
// AddArray decodes the next key to a UnmarshalerJSONArray.
530535
func (dec *Decoder) AddArray(v UnmarshalerJSONArray) error {
531536
return dec.Array(v)
532537
}
533538

539+
// AddArray decodes the next key to a UnmarshalerJSONArray.
540+
func (dec *Decoder) AddArrayNull(v UnmarshalerJSONArray) error {
541+
return dec.ArrayNull(v)
542+
}
543+
534544
// AddInterface decodes the next key to a interface{}.
535545
func (dec *Decoder) AddInterface(v *interface{}) error {
536546
return dec.Interface(v)
@@ -870,9 +880,44 @@ func (dec *Decoder) Object(value UnmarshalerJSONObject) error {
870880
return nil
871881
}
872882

883+
// ObjectNull decodes the next key to a UnmarshalerJSONObject.
884+
// v should be a pointer to an UnmarshalerJSONObject,
885+
// if `null` value is encountered in JSON, it will leave the value v untouched,
886+
// else it will create a new instance of the UnmarshalerJSONObject behind v.
887+
func (dec *Decoder) ObjectNull(v interface{}) error {
888+
initialKeysDone := dec.keysDone
889+
initialChild := dec.child
890+
dec.keysDone = 0
891+
dec.called = 0
892+
dec.child |= 1
893+
newCursor, err := dec.decodeObjectNull(v)
894+
if err != nil {
895+
return err
896+
}
897+
dec.cursor = newCursor
898+
dec.keysDone = initialKeysDone
899+
dec.child = initialChild
900+
dec.called |= 1
901+
return nil
902+
}
903+
873904
// Array decodes the next key to a UnmarshalerJSONArray.
874-
func (dec *Decoder) Array(value UnmarshalerJSONArray) error {
875-
newCursor, err := dec.decodeArray(value)
905+
func (dec *Decoder) Array(v UnmarshalerJSONArray) error {
906+
newCursor, err := dec.decodeArray(v)
907+
if err != nil {
908+
return err
909+
}
910+
dec.cursor = newCursor
911+
dec.called |= 1
912+
return nil
913+
}
914+
915+
// ArrayNull decodes the next key to a UnmarshalerJSONArray.
916+
// v should be a pointer to an UnmarshalerJSONArray,
917+
// if `null` value is encountered in JSON, it will leave the value v untouched,
918+
// else it will create a new instance of the UnmarshalerJSONArray behind v.
919+
func (dec *Decoder) ArrayNull(v interface{}) error {
920+
newCursor, err := dec.decodeArrayNull(v)
876921
if err != nil {
877922
return err
878923
}
@@ -891,6 +936,17 @@ func (dec *Decoder) Interface(value *interface{}) error {
891936
return nil
892937
}
893938

939+
// Array decodes the next key to a UnmarshalerJSONArray.
940+
// func (dec *Decoder) ArrayNull(factory func() UnmarshalerJSONArray) error {
941+
// newCursor, err := dec.decodeArrayNull(factory)
942+
// if err != nil {
943+
// return err
944+
// }
945+
// dec.cursor = newCursor
946+
// dec.called |= 1
947+
// return nil
948+
// }
949+
894950
// Non exported
895951

896952
func isDigit(b byte) bool {

decode_array.go

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package gojay
22

3+
import "reflect"
4+
35
// DecodeArray reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
46
//
57
// v must implement UnmarshalerJSONArray.
@@ -13,19 +15,79 @@ func (dec *Decoder) DecodeArray(arr UnmarshalerJSONArray) error {
1315
return err
1416
}
1517
func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
18+
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
19+
switch dec.data[dec.cursor] {
20+
case ' ', '\n', '\t', '\r', ',':
21+
continue
22+
case '[':
23+
dec.cursor = dec.cursor + 1
24+
// array is open, char is not space start readings
25+
for dec.nextChar() != 0 {
26+
// closing array
27+
if dec.data[dec.cursor] == ']' {
28+
dec.cursor = dec.cursor + 1
29+
return dec.cursor, nil
30+
}
31+
// calling unmarshall function for each element of the slice
32+
err := arr.UnmarshalJSONArray(dec)
33+
if err != nil {
34+
return 0, err
35+
}
36+
}
37+
return 0, dec.raiseInvalidJSONErr(dec.cursor)
38+
case 'n':
39+
// is null
40+
dec.cursor++
41+
err := dec.assertNull()
42+
if err != nil {
43+
return 0, err
44+
}
45+
dec.cursor++
46+
return dec.cursor, nil
47+
case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
48+
// can't unmarshall to struct
49+
// we skip array and set Error
50+
dec.err = dec.makeInvalidUnmarshalErr(arr)
51+
err := dec.skipData()
52+
if err != nil {
53+
return 0, err
54+
}
55+
return dec.cursor, nil
56+
default:
57+
return 0, dec.raiseInvalidJSONErr(dec.cursor)
58+
}
59+
}
60+
return 0, dec.raiseInvalidJSONErr(dec.cursor)
61+
}
62+
func (dec *Decoder) decodeArrayNull(v interface{}) (int, error) {
63+
vv := reflect.ValueOf(v)
64+
vvt := vv.Type()
65+
if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr {
66+
dec.err = ErrUnmarshalPtrExpected
67+
return 0, dec.err
68+
}
1669
// not an array not an error, but do not know what to do
1770
// do not check syntax
1871
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
1972
switch dec.data[dec.cursor] {
2073
case ' ', '\n', '\t', '\r', ',':
2174
continue
2275
case '[':
23-
n := 0
2476
dec.cursor = dec.cursor + 1
77+
// create our new type
78+
elt := vv.Elem()
79+
n := reflect.New(elt.Type().Elem())
80+
var arr UnmarshalerJSONArray
81+
var ok bool
82+
if arr, ok = n.Interface().(UnmarshalerJSONArray); !ok {
83+
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil))
84+
return 0, dec.err
85+
}
2586
// array is open, char is not space start readings
2687
for dec.nextChar() != 0 {
2788
// closing array
2889
if dec.data[dec.cursor] == ']' {
90+
elt.Set(n)
2991
dec.cursor = dec.cursor + 1
3092
return dec.cursor, nil
3193
}
@@ -34,7 +96,6 @@ func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
3496
if err != nil {
3597
return 0, err
3698
}
37-
n++
3899
}
39100
return 0, dec.raiseInvalidJSONErr(dec.cursor)
40101
case 'n':
@@ -49,7 +110,7 @@ func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
49110
case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
50111
// can't unmarshall to struct
51112
// we skip array and set Error
52-
dec.err = dec.makeInvalidUnmarshalErr(arr)
113+
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONArray)(nil))
53114
err := dec.skipData()
54115
if err != nil {
55116
return 0, err

decode_array_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,49 @@ func TestSliceObjects(t *testing.T) {
371371
}
372372
}
373373

374+
type ArrayNull []string
375+
376+
func (a *ArrayNull) UnmarshalJSONArray(dec *Decoder) error {
377+
var str string
378+
if err := dec.String(&str); err != nil {
379+
return err
380+
}
381+
*a = append(*a, str)
382+
return nil
383+
}
384+
385+
type ObjectArrayNull struct {
386+
SubArray *ArrayNull
387+
}
388+
389+
func (o *ObjectArrayNull) UnmarshalJSONObject(dec *Decoder, k string) error {
390+
switch k {
391+
case "subarray":
392+
return dec.ArrayNull(&o.SubArray)
393+
}
394+
return nil
395+
}
396+
397+
func (o *ObjectArrayNull) NKeys() int {
398+
return 1
399+
}
400+
401+
func TestDecodeArrayNullPtr(t *testing.T) {
402+
t.Run("sub obj should not be nil", func(t *testing.T) {
403+
var o = &ObjectArrayNull{}
404+
var err = UnmarshalJSONObject([]byte(`{"subarray": ["test"]}`), o)
405+
assert.Nil(t, err)
406+
assert.NotNil(t, o.SubArray)
407+
assert.Len(t, *o.SubArray, 1)
408+
})
409+
t.Run("sub array should be nil", func(t *testing.T) {
410+
var o = &ObjectArrayNull{}
411+
var err = UnmarshalJSONObject([]byte(`{"subarray": null}`), o)
412+
assert.Nil(t, err)
413+
assert.Nil(t, o.SubArray)
414+
})
415+
}
416+
374417
type testChannelArray chan *TestObj
375418

376419
func (c *testChannelArray) UnmarshalJSONArray(dec *Decoder) error {

decode_object.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package gojay
22

33
import (
4+
"reflect"
45
"unsafe"
56
)
67

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

104+
func (dec *Decoder) decodeObjectNull(v interface{}) (int, error) {
105+
// make sure the value is a pointer
106+
vv := reflect.ValueOf(v)
107+
vvt := vv.Type()
108+
if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr {
109+
dec.err = ErrUnmarshalPtrExpected
110+
return 0, dec.err
111+
}
112+
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
113+
switch dec.data[dec.cursor] {
114+
case ' ', '\n', '\t', '\r', ',':
115+
case '{':
116+
elt := vv.Elem()
117+
n := reflect.New(elt.Type().Elem())
118+
elt.Set(n)
119+
var j UnmarshalerJSONObject
120+
var ok bool
121+
if j, ok = n.Interface().(UnmarshalerJSONObject); !ok {
122+
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil))
123+
return 0, dec.err
124+
}
125+
keys := j.NKeys()
126+
dec.cursor = dec.cursor + 1
127+
// if keys is zero we will parse all keys
128+
// we run two loops for micro optimization
129+
if keys == 0 {
130+
for dec.cursor < dec.length || dec.read() {
131+
k, done, err := dec.nextKey()
132+
if err != nil {
133+
return 0, err
134+
} else if done {
135+
return dec.cursor, nil
136+
}
137+
err = j.UnmarshalJSONObject(dec, k)
138+
if err != nil {
139+
dec.err = err
140+
return 0, err
141+
} else if dec.called&1 == 0 {
142+
err := dec.skipData()
143+
if err != nil {
144+
return 0, err
145+
}
146+
} else {
147+
dec.keysDone++
148+
}
149+
dec.called &= 0
150+
}
151+
} else {
152+
for (dec.cursor < dec.length || dec.read()) && dec.keysDone < keys {
153+
k, done, err := dec.nextKey()
154+
if err != nil {
155+
return 0, err
156+
} else if done {
157+
return dec.cursor, nil
158+
}
159+
err = j.UnmarshalJSONObject(dec, k)
160+
if err != nil {
161+
dec.err = err
162+
return 0, err
163+
} else if dec.called&1 == 0 {
164+
err := dec.skipData()
165+
if err != nil {
166+
return 0, err
167+
}
168+
} else {
169+
dec.keysDone++
170+
}
171+
dec.called &= 0
172+
}
173+
}
174+
// will get to that point when keysDone is not lower than keys anymore
175+
// in that case, we make sure cursor goes to the end of object, but we skip
176+
// unmarshalling
177+
if dec.child&1 != 0 {
178+
end, err := dec.skipObject()
179+
dec.cursor = end
180+
return dec.cursor, err
181+
}
182+
return dec.cursor, nil
183+
case 'n':
184+
dec.cursor++
185+
err := dec.assertNull()
186+
if err != nil {
187+
return 0, err
188+
}
189+
dec.cursor++
190+
return dec.cursor, nil
191+
default:
192+
// can't unmarshal to struct
193+
dec.err = dec.makeInvalidUnmarshalErr((UnmarshalerJSONObject)(nil))
194+
err := dec.skipData()
195+
if err != nil {
196+
return 0, err
197+
}
198+
return dec.cursor, nil
199+
}
200+
}
201+
return 0, dec.raiseInvalidJSONErr(dec.cursor)
202+
}
203+
103204
func (dec *Decoder) skipObject() (int, error) {
104205
var objectsOpen = 1
105206
var objectsClosed = 0

0 commit comments

Comments
 (0)