gojay

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

commit 7b5e7ace256a95ad5c478070fb89fd175638e371
parent aa40cf1b0005c69a3fe5a1c172d73590c0da6d17
Author: francoispqt <francois@parquet.ninja>
Date:   Wed, 29 Aug 2018 13:16:43 +0800

add ObjectKeyWithKeys and ObjectWithKeys methods to encode items with specific keys

Diffstat:
Mdecode_object.go | 4----
Mencode_object.go | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mencode_object_test.go | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 176 insertions(+), 4 deletions(-)

diff --git a/decode_object.go b/decode_object.go @@ -5,10 +5,6 @@ import ( "unsafe" ) -type IsNiler interface { - IsNil() bool -} - // DecodeObject reads the next JSON-encoded value from its input and stores it in the value pointed to by v. // // v must implement UnmarshalerJSONObject. diff --git a/encode_object.go b/encode_object.go @@ -120,6 +120,35 @@ func (enc *Encoder) Object(v MarshalerJSONObject) { enc.writeByte('}') } +// ObjectWithKeys adds an object to be encoded, must be used inside a slice or array encoding (does not encode a key) +// value must implement MarshalerJSONObject. It will only encode the keys in keys. +func (enc *Encoder) ObjectWithKeys(v MarshalerJSONObject, keys []string) { + if v.IsNil() { + enc.grow(2) + r := enc.getPreviousRune() + if r != '{' && r != '[' { + enc.writeByte(',') + } + enc.writeByte('{') + enc.writeByte('}') + return + } + enc.grow(4) + r := enc.getPreviousRune() + if r != '[' { + enc.writeByte(',') + } + enc.writeByte('{') + var origKeys = enc.keys + var origHasKeys = enc.hasKeys + enc.hasKeys = true + enc.keys = keys + v.MarshalJSONObject(enc) + enc.hasKeys = origHasKeys + enc.keys = origKeys + enc.writeByte('}') +} + // ObjectOmitEmpty 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 @@ -187,6 +216,44 @@ func (enc *Encoder) ObjectKey(key string, value MarshalerJSONObject) { enc.writeByte('}') } +// ObjectKeyWithKeys adds a struct to be encoded, must be used inside an object as it will encode a key. +// Value must implement MarshalerJSONObject. It will only encode the keys in keys. +func (enc *Encoder) ObjectKeyWithKeys(key string, value MarshalerJSONObject, keys []string) { + if enc.hasKeys { + if !enc.keyExists(key) { + return + } + } + if value.IsNil() { + enc.grow(2 + len(key)) + r := enc.getPreviousRune() + if r != '{' { + enc.writeByte(',') + } + enc.writeByte('"') + enc.writeStringEscape(key) + enc.writeBytes(objKeyObj) + enc.writeByte('}') + return + } + enc.grow(5 + len(key)) + r := enc.getPreviousRune() + if r != '{' { + enc.writeByte(',') + } + enc.writeByte('"') + enc.writeStringEscape(key) + enc.writeBytes(objKeyObj) + var origKeys = enc.keys + var origHasKeys = enc.hasKeys + enc.hasKeys = true + enc.keys = keys + value.MarshalJSONObject(enc) + enc.hasKeys = origHasKeys + enc.keys = origKeys + enc.writeByte('}') +} + // ObjectKeyOmitEmpty 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 diff --git a/encode_object_test.go b/encode_object_test.go @@ -565,6 +565,11 @@ func (o *ObjectWithKeys) IsNil() bool { return o == nil } +type NilObject struct{} + +func (n *NilObject) MarshalJSONObject(enc *Encoder) {} +func (n *NilObject) IsNil() bool { return true } + func TestEncodeObjectWithKeys(t *testing.T) { t.Run( "should not encode any key", @@ -620,4 +625,108 @@ func TestEncodeObjectWithKeys(t *testing.T) { assert.NotNil(t, err, "Error should not be nil") assert.Equal(t, "Invalid type struct {} provided to Marshal", err.Error(), "err.Error() should be 'Invalid type struct {} provided to Marshal'") }) + t.Run("encode-object-with-keys", func(t *testing.T) { + b := &strings.Builder{} + enc := NewEncoder(b) + err := enc.EncodeObjectKeys(EncodeObjectFunc(func(enc *Encoder) { + enc.ObjectKeyWithKeys("test", EncodeObjectFunc(func(enc *Encoder) { + enc.StringKey("test", "hello") + enc.StringKey("test2", "hello") + }), []string{"test"}) + }), []string{}) + assert.Nil(t, err, "Error should not be nil") + assert.Equal(t, `{}`, b.String()) + }) + t.Run("encode-object-with-keys", func(t *testing.T) { + b := &strings.Builder{} + enc := NewEncoder(b) + err := enc.EncodeObject(EncodeObjectFunc(func(enc *Encoder) { + enc.ObjectKeyWithKeys("test", EncodeObjectFunc(func(enc *Encoder) { + enc.keys = nil + enc.StringKey("test", "hello") + enc.StringKey("test2", "hello") + }), []string{"test"}) + })) + assert.Nil(t, err, "Error should not be nil") + assert.Equal(t, `{"test":{}}`, b.String()) + }) + t.Run("encode-object-with-keys", func(t *testing.T) { + b := &strings.Builder{} + enc := NewEncoder(b) + err := enc.EncodeObject(EncodeObjectFunc(func(enc *Encoder) { + enc.ObjectKeyWithKeys("test", EncodeObjectFunc(func(enc *Encoder) { + enc.StringKey("test", "hello") + enc.StringKey("test2", "hello") + }), []string{"test"}) + })) + assert.Nil(t, err, "Error should not be nil") + assert.Equal(t, `{"test":{"test":"hello"}}`, b.String()) + }) + t.Run("encode-object-with-keys", func(t *testing.T) { + b := &strings.Builder{} + enc := NewEncoder(b) + err := enc.EncodeObject(EncodeObjectFunc(func(enc *Encoder) { + enc.writeByte(' ') + enc.ObjectKeyWithKeys("test", EncodeObjectFunc(func(enc *Encoder) { + enc.StringKey("test", "hello") + enc.StringKey("test2", "hello") + }), []string{"test"}) + })) + assert.Nil(t, err, "Error should not be nil") + assert.Equal(t, `{ ,"test":{"test":"hello"}}`, b.String()) + }) + t.Run("encode-object-with-keys", func(t *testing.T) { + b := &strings.Builder{} + enc := NewEncoder(b) + err := enc.EncodeObject(EncodeObjectFunc(func(enc *Encoder) { + enc.writeByte(' ') + enc.ObjectKeyWithKeys("test", &NilObject{}, []string{}) + })) + assert.Nil(t, err, "Error should not be nil") + assert.Equal(t, `{ ,"test":{}}`, b.String()) + }) + t.Run("encode-object-with-keys", func(t *testing.T) { + b := &strings.Builder{} + enc := NewEncoder(b) + err := enc.EncodeArray(EncodeArrayFunc(func(enc *Encoder) { + enc.ObjectWithKeys(EncodeObjectFunc(func(enc *Encoder) { + enc.StringKey("test", "hello") + enc.StringKey("test2", "hello") + }), []string{"test"}) + })) + assert.Nil(t, err, "Error should not be nil") + assert.Equal(t, `[{"test":"hello"}]`, b.String()) + }) + t.Run("encode-object-with-keys", func(t *testing.T) { + b := &strings.Builder{} + enc := NewEncoder(b) + err := enc.EncodeArray(EncodeArrayFunc(func(enc *Encoder) { + enc.writeByte(' ') + enc.ObjectWithKeys(EncodeObjectFunc(func(enc *Encoder) { + enc.StringKey("test", "hello") + enc.StringKey("test2", "hello") + }), []string{"test"}) + })) + assert.Nil(t, err, "Error should not be nil") + assert.Equal(t, `[ ,{"test":"hello"}]`, b.String()) + }) + t.Run("encode-object-with-keys", func(t *testing.T) { + b := &strings.Builder{} + enc := NewEncoder(b) + err := enc.EncodeArray(EncodeArrayFunc(func(enc *Encoder) { + enc.ObjectWithKeys(&NilObject{}, []string{}) + })) + assert.Nil(t, err, "Error should not be nil") + assert.Equal(t, `[{}]`, b.String()) + }) + t.Run("encode-object-with-keys", func(t *testing.T) { + b := &strings.Builder{} + enc := NewEncoder(b) + err := enc.EncodeArray(EncodeArrayFunc(func(enc *Encoder) { + enc.writeByte(' ') + enc.ObjectWithKeys(&NilObject{}, []string{}) + })) + assert.Nil(t, err, "Error should not be nil") + assert.Equal(t, `[ ,{}]`, b.String()) + }) }