gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit a5068885b61dc3bbf5c753d34a3e2647772d3a00
parent 024cbd81efc4632394c4c7422263c5f7e09ec8cc
Author: Francois Parquet <francois.parquet@gmail.com>
Date:   Tue, 21 Aug 2018 18:07:18 +0800

Merge pull request #63 from francoispqt/add-marshal-null-empty-methods

add handling of null empty and add null methods
Diffstat:
Mencode.go | 8+++++---
Mencode_array.go | 52++++++++++++++++++++++++++++++++++++++++++++++++++--
Mencode_array_test.go | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_bool.go | 45++++++++++++++++++++++++++++++++++++++++++++-
Mencode_bool_test.go | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aencode_null.go | 34++++++++++++++++++++++++++++++++++
Aencode_null_test.go | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_number_float.go | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_number_float_test.go | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_number_int.go | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_number_int_test.go | 300+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_number_uint.go | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_number_uint_test.go | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_object.go | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_object_test.go | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_sqlnull.go | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_sqlnull_test.go | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_string.go | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_string_test.go | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
19 files changed, 1951 insertions(+), 6 deletions(-)

diff --git a/encode.go b/encode.go @@ -1,12 +1,14 @@ package gojay import ( - "io" + "encoding/json" "fmt" + "io" "reflect" - "encoding/json" ) +var nullBytes = []byte("null") + // MarshalJSONObject returns the JSON encoding of v. // // It takes a struct implementing Marshaler to a JSON slice of byte @@ -178,7 +180,7 @@ func marshal(v interface{}, any bool) ([]byte, error) { return nil, InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, reflect.TypeOf(vt).String())) } - } () + }() enc.Release() return buf, err diff --git a/encode_array.go b/encode_array.go @@ -28,11 +28,17 @@ func (enc *Encoder) AddArray(v MarshalerJSONArray) { } // AddArrayOmitEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) -// value must implement Marshaler +// value must implement MarshalerAddArrayOmitEmpty func (enc *Encoder) AddArrayOmitEmpty(v MarshalerJSONArray) { enc.ArrayOmitEmpty(v) } +// AddArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) +// value must implement Marshaler, if v is empty, `null` will be encoded` +func (enc *Encoder) AddArrayNullEmpty(v MarshalerJSONArray) { + enc.ArrayNullEmpty(v) +} + // AddArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key // value must implement Marshaler func (enc *Encoder) AddArrayKey(key string, v MarshalerJSONArray) { @@ -45,6 +51,12 @@ func (enc *Encoder) AddArrayKeyOmitEmpty(key string, v MarshalerJSONArray) { enc.ArrayKeyOmitEmpty(key, v) } +// AddArrayKeyNullEmpty adds an array or slice to be encoded and skips it if it is nil. +// Must be called inside an object as it will encode a key. `null` will be encoded` +func (enc *Encoder) AddArrayKeyNullEmpty(key string, v MarshalerJSONArray) { + enc.ArrayKeyNullEmpty(key, v) +} + // Array adds an implementation of MarshalerJSONArray to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement Marshaler func (enc *Encoder) Array(v MarshalerJSONArray) { @@ -84,6 +96,23 @@ func (enc *Encoder) ArrayOmitEmpty(v MarshalerJSONArray) { enc.writeByte(']') } +// ArrayNullEmpty adds an array or slice to be encoded, must be used inside a slice or array encoding (does not encode a key) +// value must implement Marshaler +func (enc *Encoder) ArrayNullEmpty(v MarshalerJSONArray) { + enc.grow(4) + r := enc.getPreviousRune() + if r != '[' { + enc.writeByte(',') + } + if v.IsNil() { + enc.writeBytes(nullBytes) + return + } + enc.writeByte('[') + v.MarshalJSONArray(enc) + enc.writeByte(']') +} + // ArrayKey adds an array or slice to be encoded, must be used inside an object as it will encode a key // value must implement Marshaler func (enc *Encoder) ArrayKey(key string, v MarshalerJSONArray) { @@ -111,7 +140,7 @@ func (enc *Encoder) ArrayKey(key string, v MarshalerJSONArray) { enc.writeByte(']') } -// ArrayKeyOmitEmpty adds an array or slice to be encoded and skips it if it is nil. +// ArrayKeyOmitEmpty adds an array or slice to be encoded and skips if it is nil. // Must be called inside an object as it will encode a key. func (enc *Encoder) ArrayKeyOmitEmpty(key string, v MarshalerJSONArray) { if v.IsNil() { @@ -129,6 +158,25 @@ func (enc *Encoder) ArrayKeyOmitEmpty(key string, v MarshalerJSONArray) { enc.writeByte(']') } +// ArrayKeyNullEmpty adds an array or slice to be encoded and encodes `null`` if it is nil. +// Must be called inside an object as it will encode a key. +func (enc *Encoder) ArrayKeyNullEmpty(key string, v MarshalerJSONArray) { + enc.grow(5 + len(key)) + r := enc.getPreviousRune() + if r != '{' { + enc.writeByte(',') + } + if v.IsNil() { + enc.writeBytes(nullBytes) + return + } + enc.writeByte('"') + enc.writeStringEscape(key) + enc.writeBytes(objKeyArr) + v.MarshalJSONArray(enc) + enc.writeByte(']') +} + // EncodeArrayFunc is a custom func type implementing MarshaleArray. // Use it to cast a func(*Encoder) to Marshal an object. // diff --git a/encode_array_test.go b/encode_array_test.go @@ -336,3 +336,57 @@ func TestEncoderArrayFunc(t *testing.T) { var f EncodeArrayFunc assert.True(t, f.IsNil()) } + +func TestEncodeArrayNullEmpty(t *testing.T) { + var testCases = []struct { + name, baseJSON, expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,["foo"]`, + }, + { + name: "basic 1st elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,["foo"]`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddArrayNullEmpty(&TestEncodingArrStrings{}) + enc.ArrayNullEmpty(&TestEncodingArrStrings{"foo"}) + }) + } +} + +func TestEncodeArrayKeyNullEmpty(t *testing.T) { + var testCases = []struct { + name, baseJSON, expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":["foo"]`, + }, + { + name: "basic 1st elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":["foo"]`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddArrayKeyNullEmpty("foo", &TestEncodingArrStrings{}) + enc.ArrayKeyNullEmpty("bar", &TestEncodingArrStrings{"foo"}) + }) + } +} diff --git a/encode_bool.go b/encode_bool.go @@ -37,17 +37,28 @@ func (enc *Encoder) AddBoolOmitEmpty(v bool) { enc.BoolOmitEmpty(v) } +// AddBoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddBoolNullEmpty(v bool) { + enc.BoolNullEmpty(v) +} + // AddBoolKey adds a bool to be encoded, must be used inside an object as it will encode a key. func (enc *Encoder) AddBoolKey(key string, v bool) { enc.BoolKey(key, v) } -// AddBoolKeyOmitEmpty adds a bool to be encoded and skips it if it is zero value. +// AddBoolKeyOmitEmpty adds a bool to be encoded and skips if it is zero value. // Must be used inside an object as it will encode a key. func (enc *Encoder) AddBoolKeyOmitEmpty(key string, v bool) { enc.BoolKeyOmitEmpty(key, v) } +// AddBoolKeyNullEmpty adds a bool to be encoded and encodes `null` if it is zero value. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) AddBoolKeyNullEmpty(key string, v bool) { + enc.BoolKeyNullEmpty(key, v) +} + // Bool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Bool(v bool) { enc.grow(5) @@ -75,6 +86,20 @@ func (enc *Encoder) BoolOmitEmpty(v bool) { enc.writeString("true") } +// BoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) BoolNullEmpty(v bool) { + enc.grow(5) + r := enc.getPreviousRune() + if r != '[' { + enc.writeByte(',') + } + if v == false { + enc.writeBytes(nullBytes) + return + } + enc.writeString("true") +} + // BoolKey adds a bool to be encoded, must be used inside an object as it will encode a key. func (enc *Encoder) BoolKey(key string, value bool) { enc.grow(5 + len(key)) @@ -104,3 +129,21 @@ func (enc *Encoder) BoolKeyOmitEmpty(key string, v bool) { enc.writeBytes(objKey) enc.buf = strconv.AppendBool(enc.buf, v) } + +// BoolKeyNullEmpty adds a bool to be encoded and skips it if it is zero value. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) BoolKeyNullEmpty(key string, v bool) { + enc.grow(5 + len(key)) + r := enc.getPreviousRune() + if r != '{' { + enc.writeByte(',') + } + enc.writeByte('"') + enc.writeStringEscape(key) + enc.writeBytes(objKey) + if v == false { + enc.writeBytes(nullBytes) + return + } + enc.buf = strconv.AppendBool(enc.buf, v) +} diff --git a/encode_bool_test.go b/encode_bool_test.go @@ -62,3 +62,63 @@ func TestEncoderBoolErrors(t *testing.T) { assert.NotNil(t, err, "err should not be nil") }) } + +func TestEncoderBoolNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: "[null,true", + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,true`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.BoolNullEmpty(false) + enc.AddBoolNullEmpty(true) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderBoolNullKeyEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":true`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":true`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.BoolKeyNullEmpty("foo", false) + enc.AddBoolKeyNullEmpty("bar", true) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} diff --git a/encode_null.go b/encode_null.go @@ -0,0 +1,34 @@ +package gojay + +// AddNull adds a `null` to be encoded. Must be used while encoding an array.` +func (enc *Encoder) AddNull() { + enc.Null() +} + +// Null adds a `null` to be encoded. Must be used while encoding an array.` +func (enc *Encoder) Null() { + enc.grow(5) + r := enc.getPreviousRune() + if r != '[' { + enc.writeByte(',') + } + enc.writeBytes(nullBytes) +} + +// AddNullKey adds a `null` to be encoded. Must be used while encoding an array.` +func (enc *Encoder) AddNullKey(key string) { + enc.NullKey(key) +} + +// NullKey adds a `null` to be encoded. Must be used while encoding an array.` +func (enc *Encoder) NullKey(key string) { + enc.grow(5 + len(key)) + r := enc.getPreviousRune() + if r != '{' { + enc.writeByte(',') + } + enc.writeByte('"') + enc.writeStringEscape(key) + enc.writeBytes(objKey) + enc.writeBytes(nullBytes) +} diff --git a/encode_null_test.go b/encode_null_test.go @@ -0,0 +1,70 @@ +package gojay + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEncodeNull(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st element", + baseJSON: `[`, + expectedJSON: `[null,null`, + }, + { + name: "basic last element", + baseJSON: `["test"`, + expectedJSON: `["test",null,null`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.Null() + enc.AddNull() + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncodeNullKey(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st element", + baseJSON: `{`, + expectedJSON: `{"foo":null,"bar":null`, + }, + { + name: "basic last element", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":null`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.NullKey("foo") + enc.AddNullKey("bar") + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} diff --git a/encode_number_float.go b/encode_number_float.go @@ -50,6 +50,12 @@ func (enc *Encoder) AddFloatOmitEmpty(v float64) { enc.Float64OmitEmpty(v) } +// AddFloatNullEmpty adds a float64 to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) AddFloatNullEmpty(v float64) { + enc.Float64NullEmpty(v) +} + // Float adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Float(v float64) { enc.Float64(v) @@ -61,6 +67,12 @@ func (enc *Encoder) FloatOmitEmpty(v float64) { enc.Float64OmitEmpty(v) } +// FloatNullEmpty adds a float64 to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) FloatNullEmpty(v float64) { + enc.Float64NullEmpty(v) +} + // AddFloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddFloatKey(key string, v float64) { enc.Float64Key(key, v) @@ -72,6 +84,12 @@ func (enc *Encoder) AddFloatKeyOmitEmpty(key string, v float64) { enc.Float64KeyOmitEmpty(key, v) } +// AddFloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key +func (enc *Encoder) AddFloatKeyNullEmpty(key string, v float64) { + enc.Float64KeyNullEmpty(key, v) +} + // FloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) FloatKey(key string, v float64) { enc.Float64Key(key, v) @@ -83,6 +101,12 @@ func (enc *Encoder) FloatKeyOmitEmpty(key string, v float64) { enc.Float64KeyOmitEmpty(key, v) } +// FloatKeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key +func (enc *Encoder) FloatKeyNullEmpty(key string, v float64) { + enc.Float64KeyNullEmpty(key, v) +} + // AddFloat64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddFloat64(v float64) { enc.Float(v) @@ -118,6 +142,21 @@ func (enc *Encoder) Float64OmitEmpty(v float64) { enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) } +// Float64NullEmpty adds a float64 to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) Float64NullEmpty(v float64) { + enc.grow(10) + r := enc.getPreviousRune() + if r != '[' { + enc.writeByte(',') + } + if v == 0 { + enc.writeBytes(nullBytes) + return + } + enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) +} + // AddFloat64Key adds a float64 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddFloat64Key(key string, v float64) { enc.FloatKey(key, v) @@ -159,6 +198,24 @@ func (enc *Encoder) Float64KeyOmitEmpty(key string, v float64) { enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) } +// Float64KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) Float64KeyNullEmpty(key string, v float64) { + enc.grow(10 + len(key)) + r := enc.getPreviousRune() + if r != '{' { + enc.writeByte(',') + } + enc.writeByte('"') + enc.writeStringEscape(key) + enc.writeBytes(objKey) + if v == 0 { + enc.writeBytes(nullBytes) + return + } + enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64) +} + // AddFloat32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddFloat32(v float32) { enc.Float32(v) @@ -170,6 +227,12 @@ func (enc *Encoder) AddFloat32OmitEmpty(v float32) { enc.Float32OmitEmpty(v) } +// AddFloat32NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) AddFloat32NullEmpty(v float32) { + enc.Float32NullEmpty(v) +} + // Float32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Float32(v float32) { r := enc.getPreviousRune() @@ -193,6 +256,21 @@ func (enc *Encoder) Float32OmitEmpty(v float32) { enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) } +// Float32NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) Float32NullEmpty(v float32) { + enc.grow(10) + r := enc.getPreviousRune() + if r != '[' { + enc.writeByte(',') + } + if v == 0 { + enc.writeBytes(nullBytes) + return + } + enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) +} + // AddFloat32Key adds a float32 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddFloat32Key(key string, v float32) { enc.Float32Key(key, v) @@ -204,6 +282,12 @@ func (enc *Encoder) AddFloat32KeyOmitEmpty(key string, v float32) { enc.Float32KeyOmitEmpty(key, v) } +// AddFloat32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key +func (enc *Encoder) AddFloat32KeyNullEmpty(key string, v float32) { + enc.Float32KeyNullEmpty(key, v) +} + // Float32Key adds a float32 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Float32Key(key string, v float32) { enc.grow(10 + len(key)) @@ -234,3 +318,21 @@ func (enc *Encoder) Float32KeyOmitEmpty(key string, v float32) { enc.writeBytes(objKey) enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) } + +// Float32KeyNullEmpty adds a float64 to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key +func (enc *Encoder) Float32KeyNullEmpty(key string, v float32) { + enc.grow(10 + len(key)) + r := enc.getPreviousRune() + if r != '{' { + enc.writeByte(',') + } + enc.writeByte('"') + enc.writeStringEscape(key) + enc.writeBytes(objKey) + if v == 0 { + enc.writeBytes(nullBytes) + return + } + enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32) +} diff --git a/encode_number_float_test.go b/encode_number_float_test.go @@ -117,3 +117,123 @@ func TestEncoderFloat64(t *testing.T) { }) } } + +func TestEncoderFloat64NullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.FloatNullEmpty(0) + enc.AddFloatNullEmpty(1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderFloat64KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.FloatKeyNullEmpty("foo", 0) + enc.AddFloatKeyNullEmpty("bar", 1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderFloat32NullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.Float32NullEmpty(0) + enc.AddFloat32NullEmpty(1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderFloat32KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.Float32KeyNullEmpty("foo", 0) + enc.AddFloat32KeyNullEmpty("bar", 1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} diff --git a/encode_number_int.go b/encode_number_int.go @@ -51,6 +51,12 @@ func (enc *Encoder) AddIntOmitEmpty(v int) { enc.IntOmitEmpty(v) } +// AddIntNullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) AddIntNullEmpty(v int) { + enc.IntNullEmpty(v) +} + // Int adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Int(v int) { enc.grow(10) @@ -75,6 +81,21 @@ func (enc *Encoder) IntOmitEmpty(v int) { enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) } +// IntNullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) IntNullEmpty(v int) { + enc.grow(10) + r := enc.getPreviousRune() + if r != '[' { + enc.writeByte(',') + } + if v == 0 { + enc.writeBytes(nullBytes) + return + } + enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) +} + // AddIntKey adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddIntKey(key string, v int) { enc.IntKey(key, v) @@ -86,6 +107,12 @@ func (enc *Encoder) AddIntKeyOmitEmpty(key string, v int) { enc.IntKeyOmitEmpty(key, v) } +// AddIntKeyNullEmpty adds an int to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) AddIntKeyNullEmpty(key string, v int) { + enc.IntKeyNullEmpty(key, v) +} + // IntKey adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) IntKey(key string, v int) { enc.grow(10 + len(key)) @@ -116,6 +143,24 @@ func (enc *Encoder) IntKeyOmitEmpty(key string, v int) { enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) } +// IntKeyNullEmpty adds an int to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) IntKeyNullEmpty(key string, v int) { + enc.grow(10 + len(key)) + r := enc.getPreviousRune() + if r != '{' && r != '[' { + enc.writeByte(',') + } + enc.writeByte('"') + enc.writeStringEscape(key) + enc.writeBytes(objKey) + if v == 0 { + enc.writeBytes(nullBytes) + return + } + enc.buf = strconv.AppendInt(enc.buf, int64(v), 10) +} + // AddInt64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddInt64(v int64) { enc.Int64(v) @@ -127,6 +172,12 @@ func (enc *Encoder) AddInt64OmitEmpty(v int64) { enc.Int64OmitEmpty(v) } +// AddInt64NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) AddInt64NullEmpty(v int64) { + enc.Int64NullEmpty(v) +} + // Int64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Int64(v int64) { enc.grow(10) @@ -151,6 +202,21 @@ func (enc *Encoder) Int64OmitEmpty(v int64) { enc.buf = strconv.AppendInt(enc.buf, v, 10) } +// Int64NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) Int64NullEmpty(v int64) { + enc.grow(10) + r := enc.getPreviousRune() + if r != '[' { + enc.writeByte(',') + } + if v == 0 { + enc.writeBytes(nullBytes) + return + } + enc.buf = strconv.AppendInt(enc.buf, v, 10) +} + // AddInt64Key adds an int64 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddInt64Key(key string, v int64) { enc.Int64Key(key, v) @@ -162,6 +228,12 @@ func (enc *Encoder) AddInt64KeyOmitEmpty(key string, v int64) { enc.Int64KeyOmitEmpty(key, v) } +// AddInt64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) AddInt64KeyNullEmpty(key string, v int64) { + enc.Int64KeyNullEmpty(key, v) +} + // Int64Key adds an int64 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Int64Key(key string, v int64) { enc.grow(10 + len(key)) @@ -192,6 +264,24 @@ func (enc *Encoder) Int64KeyOmitEmpty(key string, v int64) { enc.buf = strconv.AppendInt(enc.buf, v, 10) } +// Int64KeyNullEmpty adds an int64 to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) Int64KeyNullEmpty(key string, v int64) { + enc.grow(10 + len(key)) + r := enc.getPreviousRune() + if r != '{' { + enc.writeByte(',') + } + enc.writeByte('"') + enc.writeStringEscape(key) + enc.writeBytes(objKey) + if v == 0 { + enc.writeBytes(nullBytes) + return + } + enc.buf = strconv.AppendInt(enc.buf, v, 10) +} + // AddInt32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddInt32(v int32) { enc.Int64(int64(v)) @@ -203,6 +293,12 @@ func (enc *Encoder) AddInt32OmitEmpty(v int32) { enc.Int64OmitEmpty(int64(v)) } +// AddInt32NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) AddInt32NullEmpty(v int32) { + enc.Int64NullEmpty(int64(v)) +} + // Int32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Int32(v int32) { enc.Int64(int64(v)) @@ -214,6 +310,12 @@ func (enc *Encoder) Int32OmitEmpty(v int32) { enc.Int64OmitEmpty(int64(v)) } +// Int32NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) Int32NullEmpty(v int32) { + enc.Int64NullEmpty(int64(v)) +} + // AddInt32Key adds an int32 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddInt32Key(key string, v int32) { enc.Int64Key(key, int64(v)) @@ -236,6 +338,12 @@ func (enc *Encoder) Int32KeyOmitEmpty(key string, v int32) { enc.Int64KeyOmitEmpty(key, int64(v)) } +// Int32KeyNullEmpty adds an int32 to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) Int32KeyNullEmpty(key string, v int32) { + enc.Int64KeyNullEmpty(key, int64(v)) +} + // AddInt16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddInt16(v int16) { enc.Int64(int64(v)) @@ -258,6 +366,12 @@ func (enc *Encoder) Int16OmitEmpty(v int16) { enc.Int64OmitEmpty(int64(v)) } +// Int16NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) Int16NullEmpty(v int16) { + enc.Int64NullEmpty(int64(v)) +} + // AddInt16Key adds an int16 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddInt16Key(key string, v int16) { enc.Int64Key(key, int64(v)) @@ -269,6 +383,12 @@ func (enc *Encoder) AddInt16KeyOmitEmpty(key string, v int16) { enc.Int64KeyOmitEmpty(key, int64(v)) } +// AddInt16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) AddInt16KeyNullEmpty(key string, v int16) { + enc.Int64KeyNullEmpty(key, int64(v)) +} + // Int16Key adds an int16 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Int16Key(key string, v int16) { enc.Int64Key(key, int64(v)) @@ -280,6 +400,12 @@ func (enc *Encoder) Int16KeyOmitEmpty(key string, v int16) { enc.Int64KeyOmitEmpty(key, int64(v)) } +// Int16KeyNullEmpty adds an int16 to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) Int16KeyNullEmpty(key string, v int16) { + enc.Int64KeyNullEmpty(key, int64(v)) +} + // AddInt8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddInt8(v int8) { enc.Int64(int64(v)) @@ -291,6 +417,12 @@ func (enc *Encoder) AddInt8OmitEmpty(v int8) { enc.Int64OmitEmpty(int64(v)) } +// AddInt8NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) AddInt8NullEmpty(v int8) { + enc.Int64NullEmpty(int64(v)) +} + // Int8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Int8(v int8) { enc.Int64(int64(v)) @@ -302,6 +434,12 @@ func (enc *Encoder) Int8OmitEmpty(v int8) { enc.Int64OmitEmpty(int64(v)) } +// Int8NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) Int8NullEmpty(v int8) { + enc.Int64NullEmpty(int64(v)) +} + // AddInt8Key adds an int8 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddInt8Key(key string, v int8) { enc.Int64Key(key, int64(v)) @@ -313,6 +451,12 @@ func (enc *Encoder) AddInt8KeyOmitEmpty(key string, v int8) { enc.Int64KeyOmitEmpty(key, int64(v)) } +// AddInt8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) AddInt8KeyNullEmpty(key string, v int8) { + enc.Int64KeyNullEmpty(key, int64(v)) +} + // Int8Key adds an int8 to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Int8Key(key string, v int8) { enc.Int64Key(key, int64(v)) @@ -323,3 +467,9 @@ func (enc *Encoder) Int8Key(key string, v int8) { func (enc *Encoder) Int8KeyOmitEmpty(key string, v int8) { enc.Int64KeyOmitEmpty(key, int64(v)) } + +// Int8KeyNullEmpty adds an int8 to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) Int8KeyNullEmpty(key string, v int8) { + enc.Int64KeyNullEmpty(key, int64(v)) +} diff --git a/encode_number_int_test.go b/encode_number_int_test.go @@ -536,3 +536,303 @@ func TestEncoderInt8(t *testing.T) { }) } } + +func TestEncoderIntNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.IntNullEmpty(0) + enc.AddIntNullEmpty(1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderIntKeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.IntKeyNullEmpty("foo", 0) + enc.AddIntKeyNullEmpty("bar", 1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderInt64NullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.Int64NullEmpty(0) + enc.AddInt64NullEmpty(1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderInt64KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.Int64KeyNullEmpty("foo", 0) + enc.AddInt64KeyNullEmpty("bar", 1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderInt32NullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.Int32NullEmpty(0) + enc.AddInt32NullEmpty(1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderInt32KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.Int32KeyNullEmpty("foo", 0) + enc.Int32KeyNullEmpty("bar", int32(1)) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderInt16NullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.Int16NullEmpty(0) + enc.Int16NullEmpty(1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderInt16KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddInt16KeyNullEmpty("foo", 0) + enc.Int16KeyNullEmpty("bar", int16(1)) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderInt8NullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddInt8NullEmpty(0) + enc.Int8NullEmpty(1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderInt8KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddInt8KeyNullEmpty("foo", 0) + enc.Int8KeyNullEmpty("bar", int8(1)) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} diff --git a/encode_number_uint.go b/encode_number_uint.go @@ -32,6 +32,12 @@ func (enc *Encoder) AddUint64OmitEmpty(v uint64) { enc.Uint64OmitEmpty(v) } +// AddUint64NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) AddUint64NullEmpty(v uint64) { + enc.Uint64NullEmpty(v) +} + // Uint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Uint64(v uint64) { enc.grow(10) @@ -56,6 +62,21 @@ func (enc *Encoder) Uint64OmitEmpty(v uint64) { enc.buf = strconv.AppendUint(enc.buf, v, 10) } +// Uint64NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) Uint64NullEmpty(v uint64) { + enc.grow(10) + r := enc.getPreviousRune() + if r != '[' { + enc.writeByte(',') + } + if v == 0 { + enc.writeBytes(nullBytes) + return + } + enc.buf = strconv.AppendUint(enc.buf, v, 10) +} + // AddUint64Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddUint64Key(key string, v uint64) { enc.Uint64Key(key, v) @@ -67,6 +88,12 @@ func (enc *Encoder) AddUint64KeyOmitEmpty(key string, v uint64) { enc.Uint64KeyOmitEmpty(key, v) } +// AddUint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) AddUint64KeyNullEmpty(key string, v uint64) { + enc.Uint64KeyNullEmpty(key, v) +} + // Uint64Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Uint64Key(key string, v uint64) { enc.grow(10 + len(key)) @@ -97,6 +124,24 @@ func (enc *Encoder) Uint64KeyOmitEmpty(key string, v uint64) { enc.buf = strconv.AppendUint(enc.buf, v, 10) } +// Uint64KeyNullEmpty adds an int to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) Uint64KeyNullEmpty(key string, v uint64) { + enc.grow(10 + len(key)) + r := enc.getPreviousRune() + if r != '{' && r != '[' { + enc.writeByte(',') + } + enc.writeByte('"') + enc.writeStringEscape(key) + enc.writeBytes(objKey) + if v == 0 { + enc.writeBytes(nullBytes) + return + } + enc.buf = strconv.AppendUint(enc.buf, v, 10) +} + // AddUint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddUint32(v uint32) { enc.Uint64(uint64(v)) @@ -108,6 +153,12 @@ func (enc *Encoder) AddUint32OmitEmpty(v uint32) { enc.Uint64OmitEmpty(uint64(v)) } +// AddUint32NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) AddUint32NullEmpty(v uint32) { + enc.Uint64NullEmpty(uint64(v)) +} + // Uint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Uint32(v uint32) { enc.Uint64(uint64(v)) @@ -119,6 +170,12 @@ func (enc *Encoder) Uint32OmitEmpty(v uint32) { enc.Uint64OmitEmpty(uint64(v)) } +// Uint32NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) Uint32NullEmpty(v uint32) { + enc.Uint64NullEmpty(uint64(v)) +} + // AddUint32Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddUint32Key(key string, v uint32) { enc.Uint64Key(key, uint64(v)) @@ -130,6 +187,12 @@ func (enc *Encoder) AddUint32KeyOmitEmpty(key string, v uint32) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } +// AddUint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) AddUint32KeyNullEmpty(key string, v uint32) { + enc.Uint64KeyNullEmpty(key, uint64(v)) +} + // Uint32Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Uint32Key(key string, v uint32) { enc.Uint64Key(key, uint64(v)) @@ -141,6 +204,12 @@ func (enc *Encoder) Uint32KeyOmitEmpty(key string, v uint32) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } +// Uint32KeyNullEmpty adds an int to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) Uint32KeyNullEmpty(key string, v uint32) { + enc.Uint64KeyNullEmpty(key, uint64(v)) +} + // AddUint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddUint16(v uint16) { enc.Uint64(uint64(v)) @@ -152,6 +221,12 @@ func (enc *Encoder) AddUint16OmitEmpty(v uint16) { enc.Uint64OmitEmpty(uint64(v)) } +// AddUint16NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) AddUint16NullEmpty(v uint16) { + enc.Uint64NullEmpty(uint64(v)) +} + // Uint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Uint16(v uint16) { enc.Uint64(uint64(v)) @@ -163,6 +238,12 @@ func (enc *Encoder) Uint16OmitEmpty(v uint16) { enc.Uint64OmitEmpty(uint64(v)) } +// Uint16NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) Uint16NullEmpty(v uint16) { + enc.Uint64NullEmpty(uint64(v)) +} + // AddUint16Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddUint16Key(key string, v uint16) { enc.Uint64Key(key, uint64(v)) @@ -174,6 +255,12 @@ func (enc *Encoder) AddUint16KeyOmitEmpty(key string, v uint16) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } +// AddUint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) AddUint16KeyNullEmpty(key string, v uint16) { + enc.Uint64KeyNullEmpty(key, uint64(v)) +} + // Uint16Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Uint16Key(key string, v uint16) { enc.Uint64Key(key, uint64(v)) @@ -185,6 +272,12 @@ func (enc *Encoder) Uint16KeyOmitEmpty(key string, v uint16) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } +// Uint16KeyNullEmpty adds an int to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) Uint16KeyNullEmpty(key string, v uint16) { + enc.Uint64KeyNullEmpty(key, uint64(v)) +} + // AddUint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) AddUint8(v uint8) { enc.Uint64(uint64(v)) @@ -196,6 +289,12 @@ func (enc *Encoder) AddUint8OmitEmpty(v uint8) { enc.Uint64OmitEmpty(uint64(v)) } +// AddUint8NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) AddUint8NullEmpty(v uint8) { + enc.Uint64NullEmpty(uint64(v)) +} + // Uint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) Uint8(v uint8) { enc.Uint64(uint64(v)) @@ -207,6 +306,12 @@ func (enc *Encoder) Uint8OmitEmpty(v uint8) { enc.Uint64OmitEmpty(uint64(v)) } +// Uint8NullEmpty adds an int to be encoded and skips it if its value is 0, +// must be used inside a slice or array encoding (does not encode a key). +func (enc *Encoder) Uint8NullEmpty(v uint8) { + enc.Uint64NullEmpty(uint64(v)) +} + // AddUint8Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddUint8Key(key string, v uint8) { enc.Uint64Key(key, uint64(v)) @@ -218,6 +323,12 @@ func (enc *Encoder) AddUint8KeyOmitEmpty(key string, v uint8) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } +// AddUint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) AddUint8KeyNullEmpty(key string, v uint8) { + enc.Uint64KeyNullEmpty(key, uint64(v)) +} + // Uint8Key adds an int to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) Uint8Key(key string, v uint8) { enc.Uint64Key(key, uint64(v)) @@ -228,3 +339,9 @@ func (enc *Encoder) Uint8Key(key string, v uint8) { func (enc *Encoder) Uint8KeyOmitEmpty(key string, v uint8) { enc.Uint64KeyOmitEmpty(key, uint64(v)) } + +// Uint8KeyNullEmpty adds an int to be encoded and skips it if its value is 0. +// Must be used inside an object as it will encode a key. +func (enc *Encoder) Uint8KeyNullEmpty(key string, v uint8) { + enc.Uint64KeyNullEmpty(key, uint64(v)) +} diff --git a/encode_number_uint_test.go b/encode_number_uint_test.go @@ -531,3 +531,243 @@ func TestEncoderUint8(t *testing.T) { }) } } + +func TestEncoderUint64NullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.Uint64NullEmpty(0) + enc.AddUint64NullEmpty(1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderUint64KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.Uint64KeyNullEmpty("foo", 0) + enc.AddUint64KeyNullEmpty("bar", 1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderUint32NullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.Uint32NullEmpty(0) + enc.AddUint32NullEmpty(1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderUint32KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddUint32KeyNullEmpty("foo", 0) + enc.Uint32KeyNullEmpty("bar", uint32(1)) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderUint16NullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddUint16NullEmpty(0) + enc.Uint16NullEmpty(1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderUint16KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddUint16KeyNullEmpty("foo", 0) + enc.Uint16KeyNullEmpty("bar", uint16(1)) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderUint8NullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddUint8NullEmpty(0) + enc.Uint8NullEmpty(1) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderUint8KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddUint8KeyNullEmpty("foo", 0) + enc.Uint8KeyNullEmpty("bar", uint8(1)) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} diff --git a/encode_object.go b/encode_object.go @@ -46,6 +46,13 @@ func (enc *Encoder) AddObjectOmitEmpty(v MarshalerJSONObject) { enc.ObjectOmitEmpty(v) } +// AddObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true. +// Must be used inside a slice or array encoding (does not encode a key) +// value must implement MarshalerJSONObject +func (enc *Encoder) AddObjectNullEmpty(v MarshalerJSONObject) { + enc.ObjectNullEmpty(v) +} + // AddObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key // value must implement MarshalerJSONObject func (enc *Encoder) AddObjectKey(key string, v MarshalerJSONObject) { @@ -59,6 +66,13 @@ func (enc *Encoder) AddObjectKeyOmitEmpty(key string, v MarshalerJSONObject) { enc.ObjectKeyOmitEmpty(key, v) } +// AddObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true. +// Must be used inside a slice or array encoding (does not encode a key) +// value must implement MarshalerJSONObject +func (enc *Encoder) AddObjectKeyNullEmpty(key string, v MarshalerJSONObject) { + enc.ObjectKeyNullEmpty(key, v) +} + // Object adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) // value must implement MarshalerJSONObject func (enc *Encoder) Object(v MarshalerJSONObject) { @@ -99,6 +113,24 @@ func (enc *Encoder) ObjectOmitEmpty(v MarshalerJSONObject) { enc.writeByte('}') } +// ObjectNullEmpty adds an object to be encoded or skips it if IsNil returns true. +// Must be used inside a slice or array encoding (does not encode a key) +// value must implement MarshalerJSONObject +func (enc *Encoder) ObjectNullEmpty(v MarshalerJSONObject) { + enc.grow(2) + r := enc.getPreviousRune() + if r != '[' { + enc.writeByte(',') + } + if v.IsNil() { + enc.writeBytes(nullBytes) + return + } + enc.writeByte('{') + v.MarshalJSONObject(enc) + enc.writeByte('}') +} + // ObjectKey adds a struct to be encoded, must be used inside an object as it will encode a key // value must implement MarshalerJSONObject func (enc *Encoder) ObjectKey(key string, value MarshalerJSONObject) { @@ -145,6 +177,27 @@ func (enc *Encoder) ObjectKeyOmitEmpty(key string, value MarshalerJSONObject) { enc.writeByte('}') } +// ObjectKeyNullEmpty adds an object to be encoded or skips it if IsNil returns true. +// Must be used inside a slice or array encoding (does not encode a key) +// value must implement MarshalerJSONObject +func (enc *Encoder) ObjectKeyNullEmpty(key string, value MarshalerJSONObject) { + enc.grow(5 + len(key)) + r := enc.getPreviousRune() + if r != '{' { + enc.writeByte(',') + } + enc.writeByte('"') + enc.writeStringEscape(key) + enc.writeBytes(objKey) + if value.IsNil() { + enc.writeBytes(nullBytes) + return + } + enc.writeByte('{') + value.MarshalJSONObject(enc) + enc.writeByte('}') +} + // EncodeObjectFunc is a custom func type implementing MarshaleObject. // Use it to cast a func(*Encoder) to Marshal an object. // diff --git a/encode_object_test.go b/encode_object_test.go @@ -419,3 +419,63 @@ func TestEncoderObjectEncodeAPIError(t *testing.T) { assert.True(t, false, "should not be called as it should have panicked") }) } + +func TestEncoderObjectKeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":{"test":"","test2":"","testInt":0,"testBool":false,"testArr":[],"testF64":0,"testF32":0,"sub":{}}`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":{"test":"","test2":"","testInt":0,"testBool":false,"testArr":[],"testF64":0,"testF32":0,"sub":{}}`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddObjectKeyNullEmpty("foo", (*TestEncoding)(nil)) + enc.ObjectKeyNullEmpty("bar", &TestEncoding{}) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderObjectNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,{"test":"","test2":"","testInt":0,"testBool":false,"testArr":[],"testF64":0,"testF32":0,"sub":{}}`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,{"test":"","test2":"","testInt":0,"testBool":false,"testArr":[],"testF64":0,"testF32":0,"sub":{}}`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddObjectNullEmpty((*TestEncoding)(nil)) + enc.ObjectNullEmpty(&TestEncoding{}) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} diff --git a/encode_sqlnull.go b/encode_sqlnull.go @@ -29,6 +29,14 @@ func (enc *Encoder) AddSQLNullStringOmitEmpty(v *sql.NullString) { } } +// AddSQLNullStringNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddSQLNullStringNullEmpty(v *sql.NullString) { + if v != nil && v.Valid { + enc.StringNullEmpty(v.String) + } +} + // AddSQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullStringKey(key string, v *sql.NullString) { enc.StringKey(key, v.String) @@ -54,6 +62,13 @@ func (enc *Encoder) SQLNullStringOmitEmpty(v *sql.NullString) { } } +// SQLNullStringNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullStringNullEmpty(v *sql.NullString) { + if v != nil && v.Valid { + enc.StringNullEmpty(v.String) + } +} + // SQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullStringKey(key string, v *sql.NullString) { enc.StringKey(key, v.String) @@ -67,6 +82,14 @@ func (enc *Encoder) SQLNullStringKeyOmitEmpty(key string, v *sql.NullString) { } } +// SQLNullStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullStringKeyNullEmpty(key string, v *sql.NullString) { + if v != nil && v.Valid { + enc.StringKeyNullEmpty(key, v.String) + } +} + // NullInt64 // EncodeSQLNullInt64 encodes a string to @@ -96,6 +119,14 @@ func (enc *Encoder) AddSQLNullInt64OmitEmpty(v *sql.NullInt64) { } } +// AddSQLNullInt64NullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddSQLNullInt64NullEmpty(v *sql.NullInt64) { + if v != nil && v.Valid { + enc.Int64NullEmpty(v.Int64) + } +} + // AddSQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullInt64Key(key string, v *sql.NullInt64) { enc.Int64Key(key, v.Int64) @@ -109,6 +140,14 @@ func (enc *Encoder) AddSQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) { } } +// AddSQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) AddSQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) { + if v != nil && v.Valid { + enc.Int64KeyNullEmpty(key, v.Int64) + } +} + // SQLNullInt64 adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullInt64(v *sql.NullInt64) { enc.Int64(v.Int64) @@ -121,6 +160,13 @@ func (enc *Encoder) SQLNullInt64OmitEmpty(v *sql.NullInt64) { } } +// SQLNullInt64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullInt64NullEmpty(v *sql.NullInt64) { + if v != nil && v.Valid { + enc.Int64NullEmpty(v.Int64) + } +} + // SQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullInt64Key(key string, v *sql.NullInt64) { enc.Int64Key(key, v.Int64) @@ -134,6 +180,14 @@ func (enc *Encoder) SQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) { } } +// SQLNullInt64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullInt64KeyNullEmpty(key string, v *sql.NullInt64) { + if v != nil && v.Valid { + enc.Int64KeyNullEmpty(key, v.Int64) + } +} + // NullFloat64 // EncodeSQLNullFloat64 encodes a string to @@ -163,6 +217,14 @@ func (enc *Encoder) AddSQLNullFloat64OmitEmpty(v *sql.NullFloat64) { } } +// AddSQLNullFloat64NullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddSQLNullFloat64NullEmpty(v *sql.NullFloat64) { + if v != nil && v.Valid { + enc.Float64NullEmpty(v.Float64) + } +} + // AddSQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddSQLNullFloat64Key(key string, v *sql.NullFloat64) { enc.Float64Key(key, v.Float64) @@ -176,6 +238,14 @@ func (enc *Encoder) AddSQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64 } } +// AddSQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) AddSQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) { + if v != nil && v.Valid { + enc.Float64KeyNullEmpty(key, v.Float64) + } +} + // SQLNullFloat64 adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullFloat64(v *sql.NullFloat64) { enc.Float64(v.Float64) @@ -188,6 +258,13 @@ func (enc *Encoder) SQLNullFloat64OmitEmpty(v *sql.NullFloat64) { } } +// SQLNullFloat64NullEmpty adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullFloat64NullEmpty(v *sql.NullFloat64) { + if v != nil && v.Valid { + enc.Float64NullEmpty(v.Float64) + } +} + // SQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullFloat64Key(key string, v *sql.NullFloat64) { enc.Float64Key(key, v.Float64) @@ -201,6 +278,14 @@ func (enc *Encoder) SQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) { } } +// SQLNullFloat64KeyNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullFloat64KeyNullEmpty(key string, v *sql.NullFloat64) { + if v != nil && v.Valid { + enc.Float64KeyNullEmpty(key, v.Float64) + } +} + // NullBool // EncodeSQLNullBool encodes a string to @@ -243,6 +328,14 @@ func (enc *Encoder) AddSQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) { } } +// AddSQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) AddSQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) { + if v != nil && v.Valid { + enc.BoolKeyNullEmpty(key, v.Bool) + } +} + // SQLNullBool adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullBool(v *sql.NullBool) { enc.Bool(v.Bool) @@ -255,6 +348,13 @@ func (enc *Encoder) SQLNullBoolOmitEmpty(v *sql.NullBool) { } } +// SQLNullBoolNullEmpty adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullBoolNullEmpty(v *sql.NullBool) { + if v != nil && v.Valid { + enc.BoolNullEmpty(v.Bool) + } +} + // SQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) SQLNullBoolKey(key string, v *sql.NullBool) { enc.BoolKey(key, v.Bool) @@ -267,3 +367,11 @@ func (enc *Encoder) SQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) { enc.BoolKeyOmitEmpty(key, v.Bool) } } + +// SQLNullBoolKeyNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullBoolKeyNullEmpty(key string, v *sql.NullBool) { + if v != nil && v.Valid { + enc.BoolKeyNullEmpty(key, v.Bool) + } +} diff --git a/encode_sqlnull_test.go b/encode_sqlnull_test.go @@ -1119,3 +1119,243 @@ func TestAddSQLNullBool(t *testing.T) { }, ) } + +func TestEncoderSQLNullStringEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,"bar"`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,"bar"`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullStringNullEmpty(&sql.NullString{"", true}) + enc.SQLNullStringNullEmpty(&sql.NullString{"bar", true}) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderSQLNullStringKeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":"bar"`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":"bar"`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.SQLNullStringKeyNullEmpty("foo", &sql.NullString{"", true}) + enc.SQLNullStringKeyNullEmpty("bar", &sql.NullString{"bar", true}) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderSQLNullBoolEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,true`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,true`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.SQLNullBoolNullEmpty(&sql.NullBool{false, true}) + enc.SQLNullBoolNullEmpty(&sql.NullBool{true, true}) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderSQLNullBoolKeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":true`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":true`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullBoolKeyNullEmpty("foo", &sql.NullBool{false, true}) + enc.SQLNullBoolKeyNullEmpty("bar", &sql.NullBool{true, true}) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderSQLNullInt64Empty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullInt64NullEmpty(&sql.NullInt64{0, true}) + enc.SQLNullInt64NullEmpty(&sql.NullInt64{1, true}) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderSQLNullInt64KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullInt64KeyNullEmpty("foo", &sql.NullInt64{0, true}) + enc.SQLNullInt64KeyNullEmpty("bar", &sql.NullInt64{1, true}) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderSQLNullFloat64Empty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,1`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullFloat64NullEmpty(&sql.NullFloat64{0, true}) + enc.SQLNullFloat64NullEmpty(&sql.NullFloat64{1, true}) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderSQLNullFloat64KeyNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":1`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":1`, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullFloat64KeyNullEmpty("foo", &sql.NullFloat64{0, true}) + enc.SQLNullFloat64KeyNullEmpty("bar", &sql.NullFloat64{1, true}) + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} diff --git a/encode_string.go b/encode_string.go @@ -41,6 +41,12 @@ func (enc *Encoder) AddStringOmitEmpty(v string) { enc.StringOmitEmpty(v) } +// AddStringNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddStringNullEmpty(v string) { + enc.StringNullEmpty(v) +} + // AddStringKey adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) AddStringKey(key, v string) { enc.StringKey(key, v) @@ -52,6 +58,12 @@ func (enc *Encoder) AddStringKeyOmitEmpty(key, v string) { enc.StringKeyOmitEmpty(key, v) } +// AddStringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) AddStringKeyNullEmpty(key, v string) { + enc.StringKeyNullEmpty(key, v) +} + // String adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) func (enc *Encoder) String(v string) { enc.grow(len(v) + 4) @@ -81,6 +93,28 @@ func (enc *Encoder) StringOmitEmpty(v string) { enc.writeByte('"') } +// StringNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) StringNullEmpty(v string) { + r := enc.getPreviousRune() + if v == "" { + if r != '[' { + enc.writeByte(',') + enc.writeBytes(nullBytes) + } else { + enc.writeBytes(nullBytes) + } + return + } + if r != '[' { + enc.writeTwoBytes(',', '"') + } else { + enc.writeByte('"') + } + enc.writeStringEscape(v) + enc.writeByte('"') +} + // StringKey adds a string to be encoded, must be used inside an object as it will encode a key func (enc *Encoder) StringKey(key, v string) { enc.grow(len(key) + len(v) + 5) @@ -114,3 +148,24 @@ func (enc *Encoder) StringKeyOmitEmpty(key, v string) { enc.writeStringEscape(v) enc.writeByte('"') } + +// StringKeyNullEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) StringKeyNullEmpty(key, v string) { + enc.grow(len(key) + len(v) + 5) + r := enc.getPreviousRune() + if r != '{' { + enc.writeTwoBytes(',', '"') + } else { + enc.writeByte('"') + } + enc.writeStringEscape(key) + enc.writeBytes(objKey) + if v == "" { + enc.writeBytes(nullBytes) + return + } + enc.writeByte('"') + enc.writeStringEscape(v) + enc.writeByte('"') +} diff --git a/encode_string_test.go b/encode_string_test.go @@ -162,3 +162,92 @@ func TestEncoderStringMarshalAPI(t *testing.T) { "Result of marshalling is different as the one expected") }) } + +func TestEncoderStringNullEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `[null,"true"`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,"true"`, + }, + { + name: "basic 2nd elem", + baseJSON: `["test"`, + expectedJSON: `["test",null,"true"`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.StringNullEmpty("") + enc.AddStringNullEmpty("true") + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderStringNullEmpty2(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "[", + expectedJSON: `["test"`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.StringNullEmpty("test") + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +} + +func TestEncoderStringNullKeyEmpty(t *testing.T) { + var testCases = []struct { + name string + baseJSON string + expectedJSON string + }{ + { + name: "basic 1st elem", + baseJSON: "{", + expectedJSON: `{"foo":null,"bar":"true"`, + }, + { + name: "basic 2nd elem", + baseJSON: `{"test":"test"`, + expectedJSON: `{"test":"test","foo":null,"bar":"true"`, + }, + } + for _, testCase := range testCases { + t.Run("true", func(t *testing.T) { + var b strings.Builder + var enc = NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.StringKeyNullEmpty("foo", "") + enc.AddStringKeyNullEmpty("bar", "true") + enc.Write() + assert.Equal(t, testCase.expectedJSON, b.String()) + }) + } +}