commit 01327d5a36628c9b867fff44a54f83bf9185a9f5
parent d3845d8d57a705dec739b5c831eefdb14c6cb749
Author: francoispqt <francois@parquet.ninja>
Date: Sun, 6 May 2018 13:24:44 +0800
add support for encoding of EmbedddedJSON
Diffstat:
4 files changed, 226 insertions(+), 0 deletions(-)
diff --git a/encode.go b/encode.go
@@ -157,6 +157,10 @@ func Marshal(v interface{}) ([]byte, error) {
enc := BorrowEncoder(nil)
defer enc.Release()
return enc.encodeFloat32(vt)
+ case *EmbeddedJSON:
+ enc := BorrowEncoder(nil)
+ defer enc.Release()
+ return enc.encodeEmbeddedJSON(vt)
default:
return nil, InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, reflect.TypeOf(vt).String()))
}
diff --git a/encode_embedded_json.go b/encode_embedded_json.go
@@ -0,0 +1,83 @@
+package gojay
+
+// EncodeEmbeddedJSON encodes an embedded JSON.
+// is basically sets the internal buf as the value pointed by v and calls the io.Writer.Write()
+func (enc *Encoder) EncodeEmbeddedJSON(v *EmbeddedJSON) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ enc.buf = *v
+ _, err := enc.write()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (enc *Encoder) encodeEmbeddedJSON(v *EmbeddedJSON) ([]byte, error) {
+ enc.writeBytes(*v)
+ return enc.buf, nil
+}
+
+// AddEmbeddedJSON adds an EmbeddedJSON to be encoded.
+//
+// It basically blindly writes the bytes to the final buffer. Therefore,
+// it expects the JSON to be of proper format.
+func (enc *Encoder) AddEmbeddedJSON(v *EmbeddedJSON) {
+ enc.grow(len(*v) + 4)
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeBytes(*v)
+}
+
+// AddEmbeddedJSONOmitEmpty adds an EmbeddedJSON to be encoded or skips it if nil pointer or empty.
+//
+// It basically blindly writes the bytes to the final buffer. Therefore,
+// it expects the JSON to be of proper format.
+func (enc *Encoder) AddEmbeddedJSONOmitEmpty(v *EmbeddedJSON) {
+ if v == nil || len(*v) == 0 {
+ return
+ }
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeBytes(*v)
+}
+
+// AddEmbeddedJSONKey adds an EmbeddedJSON and a key to be encoded.
+//
+// It basically blindly writes the bytes to the final buffer. Therefore,
+// it expects the JSON to be of proper format.
+func (enc *Encoder) AddEmbeddedJSONKey(key string, v *EmbeddedJSON) {
+ enc.grow(len(key) + len(*v) + 5)
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.writeBytes(*v)
+}
+
+// AddEmbeddedJSONKeyOmitEmpty adds an EmbeddedJSON and a key to be encoded or skips it if nil pointer or empty.
+//
+// It basically blindly writes the bytes to the final buffer. Therefore,
+// it expects the JSON to be of proper format.
+func (enc *Encoder) AddEmbeddedJSONKeyOmitEmpty(key string, v *EmbeddedJSON) {
+ if v == nil || len(*v) == 0 {
+ return
+ }
+ enc.grow(len(key) + len(*v) + 5)
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKey)
+ enc.writeBytes(*v)
+}
diff --git a/encode_embedded_json_test.go b/encode_embedded_json_test.go
@@ -0,0 +1,137 @@
+package gojay
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func (r *Request) MarshalObject(enc *Encoder) {
+ enc.AddStringKey("id", r.id)
+ enc.AddStringKey("method", r.method)
+ enc.AddEmbeddedJSONKey("params", &r.params)
+ params2 := EmbeddedJSON([]byte(``))
+ enc.AddEmbeddedJSONKeyOmitEmpty("params2", ¶ms2)
+ params3 := EmbeddedJSON([]byte(`"test"`))
+ enc.AddEmbeddedJSONKeyOmitEmpty("params3", ¶ms3)
+ enc.AddIntKey("more", r.more)
+}
+
+func (r *Request) IsNil() bool {
+ return r == nil
+}
+
+type EmbeddedJSONArr []EmbeddedJSON
+
+func (ear EmbeddedJSONArr) MarshalArray(enc *Encoder) {
+ for _, e := range ear {
+ enc.AddEmbeddedJSON(&e)
+ }
+}
+
+func (ear EmbeddedJSONArr) IsNil() bool {
+ return len(ear) == 0
+}
+
+type EmbeddedJSONOmitEmptyArr []EmbeddedJSON
+
+func (ear EmbeddedJSONOmitEmptyArr) MarshalArray(enc *Encoder) {
+ for _, e := range ear {
+ enc.AddEmbeddedJSONOmitEmpty(&e)
+ }
+}
+
+func (ear EmbeddedJSONOmitEmptyArr) IsNil() bool {
+ return len(ear) == 0
+}
+
+func TestEncodingEmbeddedJSON(t *testing.T) {
+ t.Run("basic-embedded-json", func(t *testing.T) {
+ ej := EmbeddedJSON([]byte(`"test"`))
+ b := &strings.Builder{}
+ enc := BorrowEncoder(b)
+ err := enc.Encode(&ej)
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, b.String(), `"test"`, "b should be equal to content of EmbeddedJSON")
+ })
+ t.Run("basic-embedded-json-marshal-api", func(t *testing.T) {
+ ej := EmbeddedJSON([]byte(`"test"`))
+ b, err := Marshal(&ej)
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(t, string(b), `"test"`, "b should be equal to content of EmbeddedJSON")
+ })
+ t.Run("object-embedded-json", func(t *testing.T) {
+ req := Request{
+ id: "test",
+ method: "GET",
+ params: EmbeddedJSON([]byte(`"test"`)),
+ }
+ b := &strings.Builder{}
+ enc := BorrowEncoder(b)
+ err := enc.EncodeObject(&req)
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(
+ t,
+ b.String(),
+ `{"id":"test","method":"GET","params":"test","params3":"test","more":0}`,
+ "b should be equal to content of EmbeddedJSON",
+ )
+ })
+ t.Run("array-embedded-json", func(t *testing.T) {
+ ear := EmbeddedJSONArr{
+ []byte(`"test"`),
+ []byte(`{"test":"test"}`),
+ }
+ b := &strings.Builder{}
+ enc := BorrowEncoder(b)
+ err := enc.EncodeArray(ear)
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(
+ t,
+ b.String(),
+ `["test",{"test":"test"}]`,
+ "b should be equal to content of EmbeddedJSON",
+ )
+ })
+ t.Run("array-embedded-json-omit-empty", func(t *testing.T) {
+ ear := EmbeddedJSONOmitEmptyArr{
+ []byte(`"test"`),
+ []byte(``),
+ []byte(`{"test":"test"}`),
+ []byte(``),
+ []byte(`{"test":"test"}`),
+ }
+ b := &strings.Builder{}
+ enc := BorrowEncoder(b)
+ err := enc.EncodeArray(ear)
+ assert.Nil(t, err, "err should be nil")
+ assert.Equal(
+ t,
+ b.String(),
+ `["test",{"test":"test"},{"test":"test"}]`,
+ "b should be equal to content of EmbeddedJSON",
+ )
+ })
+ t.Run("write-error", func(t *testing.T) {
+ w := TestWriterError("")
+ v := EmbeddedJSON([]byte(`"test"`))
+ enc := NewEncoder(w)
+ err := enc.EncodeEmbeddedJSON(&v)
+ assert.NotNil(t, err, "Error should not be nil")
+ assert.Equal(t, "Test Error", err.Error(), "err.Error() should be 'Test Error'")
+ })
+ t.Run("pool-error", func(t *testing.T) {
+ v := EmbeddedJSON([]byte(`"test"`))
+ enc := BorrowEncoder(nil)
+ enc.Release()
+ defer func() {
+ err := recover()
+ assert.NotNil(t, err, "err shouldnot be nil")
+ assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError")
+ assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError")
+ }()
+ _ = enc.EncodeEmbeddedJSON(&v)
+ assert.True(t, false, "should not be called as it should have panicked")
+ })
+}
diff --git a/encode_interface.go b/encode_interface.go
@@ -42,6 +42,8 @@ func (enc *Encoder) Encode(v interface{}) error {
return enc.EncodeFloat(vt)
case float32:
return enc.EncodeFloat32(vt)
+ case *EmbeddedJSON:
+ return enc.EncodeEmbeddedJSON(vt)
default:
return InvalidMarshalError(fmt.Sprintf(invalidMarshalErrorMsg, reflect.TypeOf(vt).String()))
}