commit a7c12f17ccf0509450fc93480dfc47773aa6e026
parent bf5c9d91c10918ce5ef0762a113ca076611f7281
Author: Francois Parquet <francois.parquet@gmail.com>
Date: Sun, 24 Mar 2019 02:05:14 +0800
Merge pull request #104 from francoispqt/feature/built-in-slices
Feature/built in slices
Diffstat:
10 files changed, 785 insertions(+), 6 deletions(-)
diff --git a/decode_array.go b/decode_array.go
@@ -212,7 +212,7 @@ func (dec *Decoder) AddArray(v UnmarshalerJSONArray) error {
}
// AddArrayNull decodes the JSON value within an object or an array to a UnmarshalerJSONArray.
-func (dec *Decoder) AddArrayNull(v UnmarshalerJSONArray) error {
+func (dec *Decoder) AddArrayNull(v interface{}) error {
return dec.ArrayNull(v)
}
diff --git a/decode_array_test.go b/decode_array_test.go
@@ -412,6 +412,26 @@ func TestDecodeArrayNullPtr(t *testing.T) {
assert.Nil(t, err)
assert.Nil(t, o.SubArray)
})
+ t.Run("sub array err, not closing arr", func(t *testing.T) {
+ var o = &ObjectArrayNull{}
+ var err = UnmarshalJSONObject([]byte(`{"subarray": [ `), o)
+ assert.NotNil(t, err)
+ })
+ t.Run("sub array err, invalid string", func(t *testing.T) {
+ var o = &ObjectArrayNull{}
+ var err = UnmarshalJSONObject([]byte(`{"subarray":[",]}`), o)
+ assert.NotNil(t, err)
+ })
+ t.Run("sub array err, invalid null", func(t *testing.T) {
+ var o = &ObjectArrayNull{}
+ var err = UnmarshalJSONObject([]byte(`{"subarray":nll}`), o)
+ assert.NotNil(t, err)
+ })
+ t.Run("sub array err, empty", func(t *testing.T) {
+ var o = &ObjectArrayNull{}
+ var err = UnmarshalJSONObject([]byte(`{"subarray":`), o)
+ assert.NotNil(t, err)
+ })
}
type testChannelArray chan *TestObj
diff --git a/decode_number_int_test.go b/decode_number_int_test.go
@@ -735,6 +735,12 @@ func TestDecoderInt64(t *testing.T) {
expectedResult: 0,
},
{
+ name: "before-exp-err-too-big",
+ json: "10.11231242345325435464364643e1",
+ expectedResult: 0,
+ err: true,
+ },
+ {
name: "error3",
json: "0E40",
expectedResult: 0,
@@ -1402,6 +1408,12 @@ func TestDecoderInt32(t *testing.T) {
expectedResult: -800000,
},
{
+ name: "before-exp-err-too-big",
+ json: "10.11231242345325435464364643e1",
+ expectedResult: 0,
+ err: true,
+ },
+ {
name: "exponent-err-",
json: "0.1e",
expectedResult: 0,
@@ -2017,6 +2029,11 @@ func TestDecoderInt16(t *testing.T) {
expectedResult: 120,
},
{
+ name: "exponent too big",
+ json: "1000.202302302422324435342E2",
+ err: true,
+ },
+ {
name: "basic-exponent-positive-positive-exp1",
json: "3.5e+001 ",
expectedResult: 35,
@@ -2751,6 +2768,7 @@ func TestDecoderInt8(t *testing.T) {
json: "-3e01",
expectedResult: -30,
},
+
{
name: "error3",
json: "0E40",
@@ -2765,6 +2783,12 @@ func TestDecoderInt8(t *testing.T) {
err: true,
},
{
+ name: "before-exp-err-too-big",
+ json: "10.11231242345325435464364643e1",
+ expectedResult: 0,
+ err: true,
+ },
+ {
name: "exponent-err-too-big",
json: "0.1e10000000000000000000",
expectedResult: 0,
diff --git a/decode_object_test.go b/decode_object_test.go
@@ -939,7 +939,7 @@ func (o *ObjectNull) UnmarshalJSONObject(dec *Decoder, k string) error {
case "subobject":
return dec.ObjectNull(&o.SubObject)
case "subarray":
- return dec.ArrayNull(&o.SubArray)
+ return dec.AddArrayNull(&o.SubArray)
}
return nil
}
@@ -948,6 +948,25 @@ func (o *ObjectNull) NKeys() int {
return 2
}
+type ObjectNullZeroNKeys struct {
+ SubObject *ObjectNullZeroNKeys
+ SubArray *testSliceBools
+}
+
+func (o *ObjectNullZeroNKeys) UnmarshalJSONObject(dec *Decoder, k string) error {
+ switch k {
+ case "subobject":
+ return dec.AddObjectNull(&o.SubObject)
+ case "subarray":
+ return dec.AddArrayNull(&o.SubArray)
+ }
+ return nil
+}
+
+func (o *ObjectNullZeroNKeys) NKeys() int {
+ return 0
+}
+
func TestDecodeObjectNull(t *testing.T) {
t.Run("sub obj should not be nil", func(t *testing.T) {
var o = &ObjectNull{}
@@ -986,6 +1005,49 @@ func TestDecodeObjectNull(t *testing.T) {
},
)
t.Run(
+ "skip data",
+ func(t *testing.T) {
+ var o = &ObjectNull{}
+ var err = UnmarshalJSONObject([]byte(`{
+ "subobject": {
+ "subobject": {},
+ "subarray": [],
+ "subarray": [],
+ "skipped": ""
+ }
+ }`), DecodeObjectFunc(func(dec *Decoder, k string) error {
+ return dec.ObjectNull(&o.SubObject)
+ }))
+ assert.Nil(t, err)
+ assert.NotNil(t, o.SubObject)
+ assert.Nil(t, o.SubArray)
+ },
+ )
+ t.Run(
+ "skip data not child",
+ func(t *testing.T) {
+ var o = &ObjectNull{}
+ var dec = NewDecoder(strings.NewReader(`{
+ "subobject": {},
+ "subarray": [],
+ "subarray": [],
+ "skipped": ""
+ }`))
+ var _, err = dec.decodeObjectNull(&o)
+ assert.Nil(t, err)
+ assert.NotNil(t, o.SubObject)
+ },
+ )
+ t.Run(
+ "err empty json",
+ func(t *testing.T) {
+ var o = &ObjectNull{}
+ var dec = NewDecoder(strings.NewReader(``))
+ var _, err = dec.decodeObjectNull(&o)
+ assert.NotNil(t, err)
+ },
+ )
+ t.Run(
"should return an error as type is not ptr",
func(t *testing.T) {
var err = UnmarshalJSONObject([]byte(`{"key":{}}`), DecodeObjectFunc(func(dec *Decoder, k string) error {
@@ -1064,6 +1126,17 @@ func TestDecodeObjectNull(t *testing.T) {
"invalid JSON for object",
func(t *testing.T) {
var o = &ObjectNull{}
+ var err = UnmarshalJSONObject([]byte(`{"subobject":{"subobject":{"a":a}`), DecodeObjectFunc(func(dec *Decoder, k string) error {
+ return dec.ObjectNull(&o.SubObject)
+ }))
+ assert.NotNil(t, err)
+ assert.IsType(t, InvalidJSONError(""), err)
+ },
+ )
+ t.Run(
+ "invalid JSON for object",
+ func(t *testing.T) {
+ var o = &ObjectNull{}
var err = UnmarshalJSONObject([]byte(`{"subobject":{"subobject":a}`), DecodeObjectFunc(func(dec *Decoder, k string) error {
return dec.ObjectNull(&o.SubObject)
}))
@@ -1074,6 +1147,17 @@ func TestDecodeObjectNull(t *testing.T) {
t.Run(
"invalid JSON for object",
func(t *testing.T) {
+ var o = &ObjectNull{}
+ var err = UnmarshalJSONObject([]byte(`{"subobject":{"subobject":{"sub}}`), DecodeObjectFunc(func(dec *Decoder, k string) error {
+ return dec.ObjectNull(&o.SubObject)
+ }))
+ assert.NotNil(t, err)
+ assert.IsType(t, InvalidJSONError(""), err)
+ },
+ )
+ t.Run(
+ "invalid JSON for object",
+ func(t *testing.T) {
var o = &testSliceBools{}
var err = UnmarshalJSONObject([]byte(`{"subobject":a`), DecodeObjectFunc(func(dec *Decoder, k string) error {
return dec.ArrayNull(&o)
@@ -1122,6 +1206,92 @@ func TestDecodeObjectNull(t *testing.T) {
assert.IsType(t, InvalidJSONError(""), err)
},
)
+ t.Run(
+ "zero nkeys, no error, two keys",
+ func(t *testing.T) {
+ var o = &ObjectNullZeroNKeys{}
+ var err = UnmarshalJSONObject([]byte(`{
+ "subobject": {
+ "subobject": {
+ "subobject":{}
+ },
+ "subarray": []
+ }
+ }`), DecodeObjectFunc(func(dec *Decoder, k string) error {
+ return dec.ObjectNull(&o.SubObject)
+ }))
+ assert.Nil(t, err)
+ },
+ )
+ t.Run(
+ "zero nkeys, no error, two keys, skip data",
+ func(t *testing.T) {
+ var o = &ObjectNullZeroNKeys{}
+ var err = UnmarshalJSONObject([]byte(`{
+ "subobject": {
+ "subobject": {
+ "subobject":{}
+ },
+ "subarray": [],
+ "skipped": 1
+ }
+ }`), DecodeObjectFunc(func(dec *Decoder, k string) error {
+ return dec.ObjectNull(&o.SubObject)
+ }))
+ assert.Nil(t, err)
+ },
+ )
+ t.Run(
+ "zero nkeys, error skip data",
+ func(t *testing.T) {
+ var o = &ObjectNullZeroNKeys{}
+ var err = UnmarshalJSONObject([]byte(`{
+ "subobject": {
+ "subobject": {
+ "subobject":{}
+ },
+ "subarray": [],
+ "skippedInvalid": "q
+ }
+ }`), DecodeObjectFunc(func(dec *Decoder, k string) error {
+ return dec.ObjectNull(&o.SubObject)
+ }))
+ assert.NotNil(t, err)
+ assert.IsType(t, InvalidJSONError(""), err)
+ },
+ )
+ t.Run(
+ "zero nkeys, error invalid json in keys",
+ func(t *testing.T) {
+ var o = &ObjectNullZeroNKeys{}
+ var err = UnmarshalJSONObject([]byte(`{
+ "subobject": {
+ "subobj
+ }
+ }`), DecodeObjectFunc(func(dec *Decoder, k string) error {
+ return dec.ObjectNull(&o.SubObject)
+ }))
+ assert.NotNil(t, err)
+ assert.IsType(t, InvalidJSONError(""), err)
+ },
+ )
+ t.Run(
+ "zero nkeys, error invalid json, sub object",
+ func(t *testing.T) {
+ var o = &ObjectNullZeroNKeys{}
+ var err = UnmarshalJSONObject([]byte(`{
+ "subobject": {
+ "subobject": {
+ "subobj
+ }
+ }
+ }`), DecodeObjectFunc(func(dec *Decoder, k string) error {
+ return dec.ObjectNull(&o.SubObject)
+ }))
+ assert.NotNil(t, err)
+ assert.IsType(t, InvalidJSONError(""), err)
+ },
+ )
}
func TestDecodeObjectComplex(t *testing.T) {
diff --git a/decode_slice.go b/decode_slice.go
@@ -0,0 +1,89 @@
+package gojay
+
+// AddSliceString unmarshals the next JSON array of strings to the given *[]string s
+func (dec *Decoder) AddSliceString(s *[]string) error {
+ return dec.SliceString(s)
+}
+
+// SliceString unmarshals the next JSON array of strings to the given *[]string s
+func (dec *Decoder) SliceString(s *[]string) error {
+ err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
+ var str string
+ if err := dec.String(&str); err != nil {
+ return err
+ }
+ *s = append(*s, str)
+ return nil
+ }))
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// AddSliceInt unmarshals the next JSON array of integers to the given *[]int s
+func (dec *Decoder) AddSliceInt(s *[]int) error {
+ return dec.SliceInt(s)
+}
+
+// SliceInt unmarshals the next JSON array of integers to the given *[]int s
+func (dec *Decoder) SliceInt(s *[]int) error {
+ err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
+ var i int
+ if err := dec.Int(&i); err != nil {
+ return err
+ }
+ *s = append(*s, i)
+ return nil
+ }))
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// AddFloat64 unmarshals the next JSON array of floats to the given *[]float64 s
+func (dec *Decoder) AddSliceFloat64(s *[]float64) error {
+ return dec.SliceFloat64(s)
+}
+
+// SliceFloat64 unmarshals the next JSON array of floats to the given *[]float64 s
+func (dec *Decoder) SliceFloat64(s *[]float64) error {
+ err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
+ var i float64
+ if err := dec.Float64(&i); err != nil {
+ return err
+ }
+ *s = append(*s, i)
+ return nil
+ }))
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// AddBool unmarshals the next JSON array of boolegers to the given *[]bool s
+func (dec *Decoder) AddSliceBool(s *[]bool) error {
+ return dec.SliceBool(s)
+}
+
+// SliceBool unmarshals the next JSON array of boolegers to the given *[]bool s
+func (dec *Decoder) SliceBool(s *[]bool) error {
+ err := dec.Array(DecodeArrayFunc(func(dec *Decoder) error {
+ var b bool
+ if err := dec.Bool(&b); err != nil {
+ return err
+ }
+ *s = append(*s, b)
+ return nil
+ }))
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/decode_slice_test.go b/decode_slice_test.go
@@ -0,0 +1,125 @@
+package gojay
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+type slicesTestObject struct {
+ sliceString []string
+ sliceInt []int
+ sliceFloat64 []float64
+ sliceBool []bool
+}
+
+func (s *slicesTestObject) UnmarshalJSONObject(dec *Decoder, k string) error {
+ switch k {
+ case "sliceString":
+ return dec.AddSliceString(&s.sliceString)
+ case "sliceInt":
+ return dec.AddSliceInt(&s.sliceInt)
+ case "sliceFloat64":
+ return dec.AddSliceFloat64(&s.sliceFloat64)
+ case "sliceBool":
+ return dec.AddSliceBool(&s.sliceBool)
+ }
+ return nil
+}
+
+func (s *slicesTestObject) NKeys() int {
+ return 4
+}
+
+func TestDecodeSlices(t *testing.T) {
+ testCases := []struct {
+ name string
+ json string
+ expectedResult slicesTestObject
+ err bool
+ }{
+ {
+ name: "basic slice string",
+ json: `{
+ "sliceString": ["foo","bar"]
+ }`,
+ expectedResult: slicesTestObject{
+ sliceString: []string{"foo", "bar"},
+ },
+ },
+ {
+ name: "basic slice bool",
+ json: `{
+ "sliceBool": [true,false]
+ }`,
+ expectedResult: slicesTestObject{
+ sliceBool: []bool{true, false},
+ },
+ },
+ {
+ name: "basic slice int",
+ json: `{
+ "sliceInt": [1,2,3]
+ }`,
+ expectedResult: slicesTestObject{
+ sliceInt: []int{1, 2, 3},
+ },
+ },
+ {
+ name: "basic slice float64",
+ json: `{
+ "sliceFloat64": [1.3,2.4,3.1]
+ }`,
+ expectedResult: slicesTestObject{
+ sliceFloat64: []float64{1.3, 2.4, 3.1},
+ },
+ },
+ {
+ name: "err slice float64",
+ json: `{
+ "sliceFloat64": [1.3",2.4,3.1]
+ }`,
+ err: true,
+ },
+ {
+ name: "err slice str",
+ json: `{
+ "sliceString": [",""]
+ }`,
+ err: true,
+ },
+ {
+ name: "err slice int",
+ json: `{
+ "sliceInt": [1t,2,3]
+ }`,
+ err: true,
+ },
+ {
+ name: "err slice bool",
+ json: `{
+ "sliceBool": [truo,false]
+ }`,
+ err: true,
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(
+ testCase.name,
+ func(t *testing.T) {
+ dec := BorrowDecoder(strings.NewReader(testCase.json))
+ var o slicesTestObject
+ err := dec.Decode(&o)
+
+ if testCase.err {
+ require.NotNil(t, err, "err should not be nil")
+ return
+ }
+ require.Nil(t, err, "err should be nil")
+ require.Equal(t, testCase.expectedResult, o)
+ },
+ )
+ }
+}
diff --git a/decode_sqlnull_test.go b/decode_sqlnull_test.go
@@ -211,13 +211,13 @@ type SQLDecodeObject struct {
func (s *SQLDecodeObject) UnmarshalJSONObject(dec *Decoder, k string) error {
switch k {
case "s":
- return dec.SQLNullString(&s.S)
+ return dec.AddSQLNullString(&s.S)
case "f":
- return dec.SQLNullFloat64(&s.F)
+ return dec.AddSQLNullFloat64(&s.F)
case "i":
- return dec.SQLNullInt64(&s.I)
+ return dec.AddSQLNullInt64(&s.I)
case "b":
- return dec.SQLNullBool(&s.B)
+ return dec.AddSQLNullBool(&s.B)
}
return nil
}
@@ -231,6 +231,7 @@ func TestDecodeSQLNullKeys(t *testing.T) {
name string
json string
expectedResult *SQLDecodeObject
+ err bool
}{
{
name: "basic all valid",
@@ -358,6 +359,42 @@ func TestDecodeSQLNullKeys(t *testing.T) {
},
},
},
+ {
+ name: "err string key",
+ json: `{
+ "s": "`,
+ err: true,
+ },
+ {
+ name: "err float key",
+ json: `{
+ "s": null,
+ "f": 1",
+ "i": null,
+ "b": null
+ }`,
+ err: true,
+ },
+ {
+ name: "err int key",
+ json: `{
+ "s": null,
+ "f": null,
+ "i": 1",
+ "b": null
+ }`,
+ err: true,
+ },
+ {
+ name: "err bool key",
+ json: `{
+ "s": null,
+ "f": null,
+ "i": null,
+ "b": tra
+ }`,
+ err: true,
+ },
}
for _, testCase := range testCases {
@@ -365,6 +402,12 @@ func TestDecodeSQLNullKeys(t *testing.T) {
var o = &SQLDecodeObject{}
var dec = NewDecoder(strings.NewReader(testCase.json))
var err = dec.Decode(o)
+
+ if testCase.err {
+ require.NotNil(t, err)
+ return
+ }
+
require.Nil(t, err)
require.Equal(
t,
diff --git a/decode_string_test.go b/decode_string_test.go
@@ -702,6 +702,14 @@ func TestDecoderSkipEscapedStringError3(t *testing.T) {
assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError")
}
+func TestDecoderSkipEscapedStringError4(t *testing.T) {
+ dec := NewDecoder(strings.NewReader(`\u12`))
+ defer dec.Release()
+ err := dec.skipEscapedString()
+ assert.NotNil(t, err, "Err must be nil")
+ assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError")
+}
+
func TestDecoderSkipStringError(t *testing.T) {
dec := NewDecoder(strings.NewReader(`invalid`))
defer dec.Release()
diff --git a/encode_slice.go b/encode_slice.go
@@ -0,0 +1,113 @@
+package gojay
+
+// AddSliceString marshals the given []string s
+func (enc *Encoder) AddSliceString(s []string) {
+ enc.SliceString(s)
+}
+
+// SliceString marshals the given []string s
+func (enc *Encoder) SliceString(s []string) {
+ enc.Array(EncodeArrayFunc(func(enc *Encoder) {
+ for _, str := range s {
+ enc.String(str)
+ }
+ }))
+}
+
+// AddSliceStringKey marshals the given []string s
+func (enc *Encoder) AddSliceStringKey(k string, s []string) {
+ enc.SliceStringKey(k, s)
+}
+
+// SliceStringKey marshals the given []string s
+func (enc *Encoder) SliceStringKey(k string, s []string) {
+ enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
+ for _, str := range s {
+ enc.String(str)
+ }
+ }))
+}
+
+// AddSliceInt marshals the given []int s
+func (enc *Encoder) AddSliceInt(s []int) {
+ enc.SliceInt(s)
+}
+
+// SliceInt marshals the given []int s
+func (enc *Encoder) SliceInt(s []int) {
+ enc.Array(EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Int(i)
+ }
+ }))
+}
+
+// AddSliceIntKey marshals the given []int s
+func (enc *Encoder) AddSliceIntKey(k string, s []int) {
+ enc.SliceIntKey(k, s)
+}
+
+// SliceIntKey marshals the given []int s
+func (enc *Encoder) SliceIntKey(k string, s []int) {
+ enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Int(i)
+ }
+ }))
+}
+
+// AddSliceFloat64 marshals the given []float64 s
+func (enc *Encoder) AddSliceFloat64(s []float64) {
+ enc.SliceFloat64(s)
+}
+
+// SliceFloat64 marshals the given []float64 s
+func (enc *Encoder) SliceFloat64(s []float64) {
+ enc.Array(EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Float64(i)
+ }
+ }))
+}
+
+// AddSliceFloat64Key marshals the given []float64 s
+func (enc *Encoder) AddSliceFloat64Key(k string, s []float64) {
+ enc.SliceFloat64Key(k, s)
+}
+
+// SliceFloat64Key marshals the given []float64 s
+func (enc *Encoder) SliceFloat64Key(k string, s []float64) {
+ enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Float64(i)
+ }
+ }))
+}
+
+// AddSliceBool marshals the given []bool s
+func (enc *Encoder) AddSliceBool(s []bool) {
+ enc.SliceBool(s)
+}
+
+// SliceBool marshals the given []bool s
+func (enc *Encoder) SliceBool(s []bool) {
+ enc.Array(EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Bool(i)
+ }
+ }))
+}
+
+// AddSliceBoolKey marshals the given []bool s
+func (enc *Encoder) AddSliceBoolKey(k string, s []bool) {
+ enc.SliceBoolKey(k, s)
+}
+
+// SliceBoolKey marshals the given []bool s
+func (enc *Encoder) SliceBoolKey(k string, s []bool) {
+ enc.ArrayKey(k, EncodeArrayFunc(func(enc *Encoder) {
+ for _, i := range s {
+ enc.Bool(i)
+ }
+ }))
+}
diff --git a/encode_slice_test.go b/encode_slice_test.go
@@ -0,0 +1,187 @@
+package gojay
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func (s *slicesTestObject) MarshalJSONObject(enc *Encoder) {
+ enc.AddSliceStringKey("sliceString", s.sliceString)
+ enc.AddSliceIntKey("sliceInt", s.sliceInt)
+ enc.AddSliceFloat64Key("sliceFloat64", s.sliceFloat64)
+ enc.AddSliceBoolKey("sliceBool", s.sliceBool)
+}
+
+func (s *slicesTestObject) IsNil() bool {
+ return s == nil
+}
+
+func TestEncodeSlices(t *testing.T) {
+ testCases := []struct {
+ name string
+ json string
+ obj slicesTestObject
+ }{
+ {
+ name: "basic slice string",
+ json: `{
+ "sliceString": ["foo","bar"],
+ "sliceInt": [],
+ "sliceFloat64": [],
+ "sliceBool": []
+ }`,
+ obj: slicesTestObject{
+ sliceString: []string{"foo", "bar"},
+ },
+ },
+ {
+ name: "basic slice bool",
+ json: `{
+ "sliceString": [],
+ "sliceInt": [],
+ "sliceFloat64": [],
+ "sliceBool": [true,false]
+ }`,
+ obj: slicesTestObject{
+ sliceBool: []bool{true, false},
+ },
+ },
+ {
+ name: "basic slice int",
+ json: `{
+ "sliceString": [],
+ "sliceFloat64": [],
+ "sliceInt": [1,2,3],
+ "sliceBool": []
+ }`,
+ obj: slicesTestObject{
+ sliceInt: []int{1, 2, 3},
+ },
+ },
+ {
+ name: "basic slice float64",
+ json: `{
+ "sliceString": [],
+ "sliceFloat64": [1.3,2.4,3.1],
+ "sliceInt": [],
+ "sliceBool": []
+ }`,
+ obj: slicesTestObject{
+ sliceFloat64: []float64{1.3, 2.4, 3.1},
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(
+ testCase.name,
+ func(t *testing.T) {
+ b := strings.Builder{}
+ enc := BorrowEncoder(&b)
+ err := enc.Encode(&testCase.obj)
+ require.Nil(t, err, "err should be nil")
+ require.JSONEq(t, testCase.json, b.String())
+ },
+ )
+ }
+}
+
+type testSliceSliceString [][]string
+
+func (t testSliceSliceString) MarshalJSONArray(enc *Encoder) {
+ for _, s := range t {
+ enc.AddSliceString(s)
+ }
+}
+
+func (t testSliceSliceString) IsNil() bool {
+ return t == nil
+}
+
+type testSliceSliceBool [][]bool
+
+func (t testSliceSliceBool) MarshalJSONArray(enc *Encoder) {
+ for _, s := range t {
+ enc.AddSliceBool(s)
+ }
+}
+
+func (t testSliceSliceBool) IsNil() bool {
+ return t == nil
+}
+
+type testSliceSliceInt [][]int
+
+func (t testSliceSliceInt) MarshalJSONArray(enc *Encoder) {
+ for _, s := range t {
+ enc.AddSliceInt(s)
+ }
+}
+
+func (t testSliceSliceInt) IsNil() bool {
+ return t == nil
+}
+
+type testSliceSliceFloat64 [][]float64
+
+func (t testSliceSliceFloat64) MarshalJSONArray(enc *Encoder) {
+ for _, s := range t {
+ enc.AddSliceFloat64(s)
+ }
+}
+
+func (t testSliceSliceFloat64) IsNil() bool {
+ return t == nil
+}
+
+func TestEncodeSliceSlices(t *testing.T) {
+ testCases := []struct {
+ name string
+ s MarshalerJSONArray
+ json string
+ }{
+ {
+ name: "slice of strings",
+ s: testSliceSliceString{
+ []string{"foo", "bar"},
+ },
+ json: `[["foo","bar"]]`,
+ },
+ {
+ name: "slice of ints",
+ s: testSliceSliceInt{
+ []int{1, 2},
+ },
+ json: `[[1,2]]`,
+ },
+ {
+ name: "slice of float",
+ s: testSliceSliceFloat64{
+ []float64{1.1, 1.2},
+ },
+ json: `[[1.1,1.2]]`,
+ },
+ {
+ name: "slice of bool",
+ s: testSliceSliceBool{
+ []bool{true, false},
+ },
+ json: `[[true,false]]`,
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(
+ testCase.name,
+ func(t *testing.T) {
+ b := strings.Builder{}
+ enc := BorrowEncoder(&b)
+ err := enc.Encode(testCase.s)
+ require.Nil(t, err, "err should be nil")
+ require.JSONEq(t, testCase.json, b.String())
+ },
+ )
+ }
+}