Skip to content

Commit

Permalink
feat: support marshal slice type if's not proto message (#7)
Browse files Browse the repository at this point in the history
* feat: support marshal slice type if's not proto message

* test: add marshal a nil slice type case
  • Loading branch information
mingqing authored Dec 20, 2020
1 parent 035ef58 commit 0cb5531
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 0 deletions.
39 changes: 39 additions & 0 deletions jsonpb.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ func (j *JSONPb) marshalTo(w io.Writer, v interface{}) error {
return (*jsonpb.Marshaler)(j).Marshal(w, p)
}

var (
// protoMessageType is stored to prevent constant lookup of the same type at runtime.
protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
)

// marshalNonProto marshals a non-message field of a protobuf message.
// This function does not correctly marshals arbitary data structure into JSON,
// but it is only capable of marshaling non-message field values of protobuf,
Expand All @@ -63,6 +68,40 @@ func (j *JSONPb) marshalNonProtoField(v interface{}) ([]byte, error) {
rv = rv.Elem()
}

if rv.Kind() == reflect.Slice {
if rv.IsNil() {
if j.EmitDefaults {
return []byte("[]"), nil
}
return []byte("null"), nil
}

if rv.Type().Elem().Implements(protoMessageType) {
var buf bytes.Buffer
err := buf.WriteByte('[')
if err != nil {
return nil, err
}
for i := 0; i < rv.Len(); i++ {
if i != 0 {
err = buf.WriteByte(',')
if err != nil {
return nil, err
}
}
if err = (*jsonpb.Marshaler)(j).Marshal(&buf, rv.Index(i).Interface().(proto.Message)); err != nil {
return nil, err
}
}
err = buf.WriteByte(']')
if err != nil {
return nil, err
}

return buf.Bytes(), nil
}
}

if rv.Kind() == reflect.Map {
m := make(map[string]*json.RawMessage)
for _, k := range rv.MapKeys() {
Expand Down
38 changes: 38 additions & 0 deletions jsonpb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package gateway

import (
"testing"
)

func TestMarshalNonProtoSliceField(t *testing.T) {
type testData struct {
List []string
}

data := testData{}
j := &JSONPb{}

t.Run("disable EmitDefaults", func(t *testing.T) {
j.EmitDefaults = false

r, err := j.Marshal(data.List)
if err != nil {
t.Errorf("Marshal failed with %v", err)
}
if want := "null"; want != string(r) {
t.Errorf("want (%v), got (%v)", want, string(r))
}
})

t.Run("enable EmitDefaults", func(t *testing.T) {
j.EmitDefaults = true

r, err := j.Marshal(data.List)
if err != nil {
t.Errorf("Marshal failed with %v", err)
}
if want := "[]"; want != string(r) {
t.Errorf("want (%v), got (%v)", want, string(r))
}
})
}

0 comments on commit 0cb5531

Please sign in to comment.