Skip to content

Commit

Permalink
Merge pull request #96 from verytable/feature/provide-decoded-array-i…
Browse files Browse the repository at this point in the history
…ndex

add Index() method to Decoder
  • Loading branch information
francoispqt committed Feb 28, 2019
2 parents 106fcfa + 75d85cb commit 90d9533
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 9 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,20 @@ func (c ChannelArray) UnmarshalJSONArray(dec *gojay.Decoder) error {
}
```

Example of implementation with an array:
```go
type testArray [3]string
// implement UnmarshalerJSONArray
func (a *testArray) UnmarshalJSONArray(dec *Decoder) error {
var str string
if err := dec.String(&str); err != nil {
return err
}
a[dec.Index()] = str
return nil
}
```

### Other types
To decode other types (string, int, int32, int64, uint32, uint64, float, booleans), you don't need to implement any interface.

Expand Down
19 changes: 10 additions & 9 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,15 +235,16 @@ type UnmarshalerJSONArray interface {

// A Decoder reads and decodes JSON values from an input stream.
type Decoder struct {
r io.Reader
data []byte
err error
isPooled byte
called byte
child byte
cursor int
length int
keysDone int
r io.Reader
data []byte
err error
isPooled byte
called byte
child byte
cursor int
length int
keysDone int
arrayIndex int
}

// Decode reads the next JSON-encoded value from the decoder's input (io.Reader) and stores it in the value pointed to by v.
Expand Down
19 changes: 19 additions & 0 deletions decode_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ func (dec *Decoder) DecodeArray(v UnmarshalerJSONArray) error {
return err
}
func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
// remember last array index in case of nested arrays
lastArrayIndex := dec.arrayIndex
dec.arrayIndex = 0
defer func() {
dec.arrayIndex = lastArrayIndex
}()
for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
switch dec.data[dec.cursor] {
case ' ', '\n', '\t', '\r', ',':
Expand All @@ -34,6 +40,7 @@ func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
if err != nil {
return 0, err
}
dec.arrayIndex++
}
return 0, dec.raiseInvalidJSONErr(dec.cursor)
case 'n':
Expand All @@ -60,6 +67,12 @@ func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) {
return 0, dec.raiseInvalidJSONErr(dec.cursor)
}
func (dec *Decoder) decodeArrayNull(v interface{}) (int, error) {
// remember last array index in case of nested arrays
lastArrayIndex := dec.arrayIndex
dec.arrayIndex = 0
defer func() {
dec.arrayIndex = lastArrayIndex
}()
vv := reflect.ValueOf(v)
vvt := vv.Type()
if vvt.Kind() != reflect.Ptr || vvt.Elem().Kind() != reflect.Ptr {
Expand Down Expand Up @@ -96,6 +109,7 @@ func (dec *Decoder) decodeArrayNull(v interface{}) (int, error) {
if err != nil {
return 0, err
}
dec.arrayIndex++
}
return 0, dec.raiseInvalidJSONErr(dec.cursor)
case 'n':
Expand Down Expand Up @@ -226,3 +240,8 @@ func (dec *Decoder) ArrayNull(v interface{}) error {
dec.called |= 1
return nil
}

// Index returns the index of an array being decoded.
func (dec *Decoder) Index() int {
return dec.arrayIndex
}
71 changes: 71 additions & 0 deletions decode_array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,3 +627,74 @@ func TestDecoderArrayFunc(t *testing.T) {
var f DecodeArrayFunc
assert.True(t, f.IsNil())
}

type testArrayStrings [3]string

func (a *testArrayStrings) UnmarshalJSONArray(dec *Decoder) error {
var str string
if err := dec.String(&str); err != nil {
return err
}
a[dec.Index()] = str
return nil
}

func TestArrayStrings(t *testing.T) {
data := []byte(`["a", "b", "c"]`)
arr := testArrayStrings{}
err := Unmarshal(data, &arr)
assert.Nil(t, err, "err must be nil")
assert.Equal(t, "a", arr[0], "arr[0] must be equal to 'a'")
assert.Equal(t, "b", arr[1], "arr[1] must be equal to 'b'")
assert.Equal(t, "c", arr[2], "arr[2] must be equal to 'c'")
}

type testSliceArraysStrings struct {
arrays []testArrayStrings
t *testing.T
}

func (s *testSliceArraysStrings) UnmarshalJSONArray(dec *Decoder) error {
var a testArrayStrings
assert.Equal(s.t, len(s.arrays), dec.Index(), "decoded array index must be equal to current slice len")
if err := dec.AddArray(&a); err != nil {
return err
}
assert.Equal(s.t, len(s.arrays), dec.Index(), "decoded array index must be equal to current slice len")
s.arrays = append(s.arrays, a)
return nil
}

func TestIndex(t *testing.T) {
testCases := []struct {
name string
json string
expectedResult []testArrayStrings
}{
{
name: "basic-test",
json: `[["a","b","c"],["1","2","3"],["x","y","z"]]`,
expectedResult: []testArrayStrings{{"a", "b", "c"}, {"1", "2", "3"}, {"x", "y", "z"}},
},
{
name: "basic-test-null",
json: `[["a","b","c"],null,["x","y","z"]]`,
expectedResult: []testArrayStrings{{"a", "b", "c"}, {"", "", ""}, {"x", "y", "z"}},
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
s := make([]testArrayStrings, 0)
dec := BorrowDecoder(strings.NewReader(testCase.json))
defer dec.Release()
a := testSliceArraysStrings{arrays: s, t: t}
err := dec.Decode(&a)
assert.Nil(t, err, "err should be nil")
assert.Zero(t, dec.Index(), "Index() must return zero after decoding")
for k, v := range testCase.expectedResult {
assert.Equal(t, v, a.arrays[k], "value at given index should be the same as expected results")
}
})
}
}

0 comments on commit 90d9533

Please sign in to comment.