Skip to content

Commit a506888

Browse files
authored
Merge pull request #63 from francoispqt/add-marshal-null-empty-methods
add handling of null empty and add null methods
2 parents 024cbd8 + c18a39a commit a506888

19 files changed

+1951
-6
lines changed

encode.go

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

33
import (
4-
"io"
4+
"encoding/json"
55
"fmt"
6+
"io"
67
"reflect"
7-
"encoding/json"
88
)
99

10+
var nullBytes = []byte("null")
11+
1012
// MarshalJSONObject returns the JSON encoding of v.
1113
//
1214
// It takes a struct implementing Marshaler to a JSON slice of byte
@@ -178,7 +180,7 @@ func marshal(v interface{}, any bool) ([]byte, error) {
178180

179181
return nil, InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, reflect.TypeOf(vt).String()))
180182
}
181-
} ()
183+
}()
182184

183185
enc.Release()
184186
return buf, err

encode_array.go

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,17 @@ func (enc *Encoder) AddArray(v MarshalerJSONArray) {
2828
}
2929

3030
// AddArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
31-
// value must implement Marshaler
31+
// value must implement MarshalerAddArrayOmitEmpty
3232
func (enc *Encoder) AddArrayOmitEmpty(v MarshalerJSONArray) {
3333
enc.ArrayOmitEmpty(v)
3434
}
3535

36+
// AddArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
37+
// value must implement Marshaler, if v is empty, `null` will be encoded`
38+
func (enc *Encoder) AddArrayNullEmpty(v MarshalerJSONArray) {
39+
enc.ArrayNullEmpty(v)
40+
}
41+
3642
// AddArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key
3743
// value must implement Marshaler
3844
func (enc *Encoder) AddArrayKey(key string, v MarshalerJSONArray) {
@@ -45,6 +51,12 @@ func (enc *Encoder) AddArrayKeyOmitEmpty(key string, v MarshalerJSONArray) {
4551
enc.ArrayKeyOmitEmpty(key, v)
4652
}
4753

54+
// AddArrayKeyNullEmpty adds an array or slice to be encoded and skips it if it is nil.
55+
// Must be called inside an object as it will encode a key. `null` will be encoded`
56+
func (enc *Encoder) AddArrayKeyNullEmpty(key string, v MarshalerJSONArray) {
57+
enc.ArrayKeyNullEmpty(key, v)
58+
}
59+
4860
// Array adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key)
4961
// value must implement Marshaler
5062
func (enc *Encoder) Array(v MarshalerJSONArray) {
@@ -84,6 +96,23 @@ func (enc *Encoder) ArrayOmitEmpty(v MarshalerJSONArray) {
8496
enc.writeByte(']')
8597
}
8698

99+
// ArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key)
100+
// value must implement Marshaler
101+
func (enc *Encoder) ArrayNullEmpty(v MarshalerJSONArray) {
102+
enc.grow(4)
103+
r := enc.getPreviousRune()
104+
if r != '[' {
105+
enc.writeByte(',')
106+
}
107+
if v.IsNil() {
108+
enc.writeBytes(nullBytes)
109+
return
110+
}
111+
enc.writeByte('[')
112+
v.MarshalJSONArray(enc)
113+
enc.writeByte(']')
114+
}
115+
87116
// ArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key
88117
// value must implement Marshaler
89118
func (enc *Encoder) ArrayKey(key string, v MarshalerJSONArray) {
@@ -111,7 +140,7 @@ func (enc *Encoder) ArrayKey(key string, v MarshalerJSONArray) {
111140
enc.writeByte(']')
112141
}
113142

114-
// ArrayKeyOmitEmpty adds an array or slice to be encoded and skips it if it is nil.
143+
// ArrayKeyOmitEmpty adds an array or slice to be encoded and skips if it is nil.
115144
// Must be called inside an object as it will encode a key.
116145
func (enc *Encoder) ArrayKeyOmitEmpty(key string, v MarshalerJSONArray) {
117146
if v.IsNil() {
@@ -129,6 +158,25 @@ func (enc *Encoder) ArrayKeyOmitEmpty(key string, v MarshalerJSONArray) {
129158
enc.writeByte(']')
130159
}
131160

161+
// ArrayKeyNullEmpty adds an array or slice to be encoded and encodes `null`` if it is nil.
162+
// Must be called inside an object as it will encode a key.
163+
func (enc *Encoder) ArrayKeyNullEmpty(key string, v MarshalerJSONArray) {
164+
enc.grow(5 + len(key))
165+
r := enc.getPreviousRune()
166+
if r != '{' {
167+
enc.writeByte(',')
168+
}
169+
if v.IsNil() {
170+
enc.writeBytes(nullBytes)
171+
return
172+
}
173+
enc.writeByte('"')
174+
enc.writeStringEscape(key)
175+
enc.writeBytes(objKeyArr)
176+
v.MarshalJSONArray(enc)
177+
enc.writeByte(']')
178+
}
179+
132180
// EncodeArrayFunc is a custom func type implementing MarshaleArray.
133181
// Use it to cast a func(*Encoder) to Marshal an object.
134182
//

encode_array_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,57 @@ func TestEncoderArrayFunc(t *testing.T) {
336336
var f EncodeArrayFunc
337337
assert.True(t, f.IsNil())
338338
}
339+
340+
func TestEncodeArrayNullEmpty(t *testing.T) {
341+
var testCases = []struct {
342+
name, baseJSON, expectedJSON string
343+
}{
344+
{
345+
name: "basic 1st elem",
346+
baseJSON: "[",
347+
expectedJSON: `[null,["foo"]`,
348+
},
349+
{
350+
name: "basic 1st elem",
351+
baseJSON: `["test"`,
352+
expectedJSON: `["test",null,["foo"]`,
353+
},
354+
}
355+
356+
for _, testCase := range testCases {
357+
t.Run(testCase.name, func(t *testing.T) {
358+
var b strings.Builder
359+
var enc = NewEncoder(&b)
360+
enc.writeString(testCase.baseJSON)
361+
enc.AddArrayNullEmpty(&TestEncodingArrStrings{})
362+
enc.ArrayNullEmpty(&TestEncodingArrStrings{"foo"})
363+
})
364+
}
365+
}
366+
367+
func TestEncodeArrayKeyNullEmpty(t *testing.T) {
368+
var testCases = []struct {
369+
name, baseJSON, expectedJSON string
370+
}{
371+
{
372+
name: "basic 1st elem",
373+
baseJSON: "{",
374+
expectedJSON: `{"foo":null,"bar":["foo"]`,
375+
},
376+
{
377+
name: "basic 1st elem",
378+
baseJSON: `{"test":"test"`,
379+
expectedJSON: `{"test":"test","foo":null,"bar":["foo"]`,
380+
},
381+
}
382+
383+
for _, testCase := range testCases {
384+
t.Run(testCase.name, func(t *testing.T) {
385+
var b strings.Builder
386+
var enc = NewEncoder(&b)
387+
enc.writeString(testCase.baseJSON)
388+
enc.AddArrayKeyNullEmpty("foo", &TestEncodingArrStrings{})
389+
enc.ArrayKeyNullEmpty("bar", &TestEncodingArrStrings{"foo"})
390+
})
391+
}
392+
}

encode_bool.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,28 @@ func (enc *Encoder) AddBoolOmitEmpty(v bool) {
3737
enc.BoolOmitEmpty(v)
3838
}
3939

40+
// AddBoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
41+
func (enc *Encoder) AddBoolNullEmpty(v bool) {
42+
enc.BoolNullEmpty(v)
43+
}
44+
4045
// AddBoolKey adds a bool to be encoded, must be used inside an object as it will encode a key.
4146
func (enc *Encoder) AddBoolKey(key string, v bool) {
4247
enc.BoolKey(key, v)
4348
}
4449

45-
// AddBoolKeyOmitEmpty adds a bool to be encoded and skips it if it is zero value.
50+
// AddBoolKeyOmitEmpty adds a bool to be encoded and skips if it is zero value.
4651
// Must be used inside an object as it will encode a key.
4752
func (enc *Encoder) AddBoolKeyOmitEmpty(key string, v bool) {
4853
enc.BoolKeyOmitEmpty(key, v)
4954
}
5055

56+
// AddBoolKeyNullEmpty adds a bool to be encoded and encodes `null` if it is zero value.
57+
// Must be used inside an object as it will encode a key.
58+
func (enc *Encoder) AddBoolKeyNullEmpty(key string, v bool) {
59+
enc.BoolKeyNullEmpty(key, v)
60+
}
61+
5162
// Bool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
5263
func (enc *Encoder) Bool(v bool) {
5364
enc.grow(5)
@@ -75,6 +86,20 @@ func (enc *Encoder) BoolOmitEmpty(v bool) {
7586
enc.writeString("true")
7687
}
7788

89+
// BoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
90+
func (enc *Encoder) BoolNullEmpty(v bool) {
91+
enc.grow(5)
92+
r := enc.getPreviousRune()
93+
if r != '[' {
94+
enc.writeByte(',')
95+
}
96+
if v == false {
97+
enc.writeBytes(nullBytes)
98+
return
99+
}
100+
enc.writeString("true")
101+
}
102+
78103
// BoolKey adds a bool to be encoded, must be used inside an object as it will encode a key.
79104
func (enc *Encoder) BoolKey(key string, value bool) {
80105
enc.grow(5 + len(key))
@@ -104,3 +129,21 @@ func (enc *Encoder) BoolKeyOmitEmpty(key string, v bool) {
104129
enc.writeBytes(objKey)
105130
enc.buf = strconv.AppendBool(enc.buf, v)
106131
}
132+
133+
// BoolKeyNullEmpty adds a bool to be encoded and skips it if it is zero value.
134+
// Must be used inside an object as it will encode a key.
135+
func (enc *Encoder) BoolKeyNullEmpty(key string, v bool) {
136+
enc.grow(5 + len(key))
137+
r := enc.getPreviousRune()
138+
if r != '{' {
139+
enc.writeByte(',')
140+
}
141+
enc.writeByte('"')
142+
enc.writeStringEscape(key)
143+
enc.writeBytes(objKey)
144+
if v == false {
145+
enc.writeBytes(nullBytes)
146+
return
147+
}
148+
enc.buf = strconv.AppendBool(enc.buf, v)
149+
}

encode_bool_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,63 @@ func TestEncoderBoolErrors(t *testing.T) {
6262
assert.NotNil(t, err, "err should not be nil")
6363
})
6464
}
65+
66+
func TestEncoderBoolNullEmpty(t *testing.T) {
67+
var testCases = []struct {
68+
name string
69+
baseJSON string
70+
expectedJSON string
71+
}{
72+
{
73+
name: "basic 1st elem",
74+
baseJSON: "[",
75+
expectedJSON: "[null,true",
76+
},
77+
{
78+
name: "basic 2nd elem",
79+
baseJSON: `["test"`,
80+
expectedJSON: `["test",null,true`,
81+
},
82+
}
83+
for _, testCase := range testCases {
84+
t.Run("true", func(t *testing.T) {
85+
var b strings.Builder
86+
var enc = NewEncoder(&b)
87+
enc.writeString(testCase.baseJSON)
88+
enc.BoolNullEmpty(false)
89+
enc.AddBoolNullEmpty(true)
90+
enc.Write()
91+
assert.Equal(t, testCase.expectedJSON, b.String())
92+
})
93+
}
94+
}
95+
96+
func TestEncoderBoolNullKeyEmpty(t *testing.T) {
97+
var testCases = []struct {
98+
name string
99+
baseJSON string
100+
expectedJSON string
101+
}{
102+
{
103+
name: "basic 1st elem",
104+
baseJSON: "{",
105+
expectedJSON: `{"foo":null,"bar":true`,
106+
},
107+
{
108+
name: "basic 2nd elem",
109+
baseJSON: `{"test":"test"`,
110+
expectedJSON: `{"test":"test","foo":null,"bar":true`,
111+
},
112+
}
113+
for _, testCase := range testCases {
114+
t.Run("true", func(t *testing.T) {
115+
var b strings.Builder
116+
var enc = NewEncoder(&b)
117+
enc.writeString(testCase.baseJSON)
118+
enc.BoolKeyNullEmpty("foo", false)
119+
enc.AddBoolKeyNullEmpty("bar", true)
120+
enc.Write()
121+
assert.Equal(t, testCase.expectedJSON, b.String())
122+
})
123+
}
124+
}

encode_null.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package gojay
2+
3+
// AddNull adds a `null` to be encoded. Must be used while encoding an array.`
4+
func (enc *Encoder) AddNull() {
5+
enc.Null()
6+
}
7+
8+
// Null adds a `null` to be encoded. Must be used while encoding an array.`
9+
func (enc *Encoder) Null() {
10+
enc.grow(5)
11+
r := enc.getPreviousRune()
12+
if r != '[' {
13+
enc.writeByte(',')
14+
}
15+
enc.writeBytes(nullBytes)
16+
}
17+
18+
// AddNullKey adds a `null` to be encoded. Must be used while encoding an array.`
19+
func (enc *Encoder) AddNullKey(key string) {
20+
enc.NullKey(key)
21+
}
22+
23+
// NullKey adds a `null` to be encoded. Must be used while encoding an array.`
24+
func (enc *Encoder) NullKey(key string) {
25+
enc.grow(5 + len(key))
26+
r := enc.getPreviousRune()
27+
if r != '{' {
28+
enc.writeByte(',')
29+
}
30+
enc.writeByte('"')
31+
enc.writeStringEscape(key)
32+
enc.writeBytes(objKey)
33+
enc.writeBytes(nullBytes)
34+
}

0 commit comments

Comments
 (0)