Skip to content

Commit a4e8f93

Browse files
jhumpcybrcodr
authored andcommitted
Support custom JSON (de)serialization in jsonpb
Introduce jsonpb JSONPBMarshaler and JSONPBUnmarshaler interfaces for custom JSON serialization and deserialization of dynamic messages. jsonpb will delegate to these interface methods passing in the default Marhaler/Unmarshaler in order to preserve the options as well as reuse these for regular protobuf values within the dynamic message.
1 parent fec3b39 commit a4e8f93

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

jsonpb/jsonpb.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,22 @@ type Marshaler struct {
7474
OrigName bool
7575
}
7676

77+
// JSONPBMarshaler is implemented by protobuf messages that customize the
78+
// way they are marshaled to JSON. Messages that implement this should
79+
// also implement JSONPBUnmarshaler so that the custom format can be
80+
// parsed.
81+
type JSONPBMarshaler interface {
82+
MarshalJSONPB(*Marshaler) ([]byte, error)
83+
}
84+
85+
// JSONPBUnmarshaler is implemented by protobuf messages that customize
86+
// the way they are unmarshaled from JSON. Messages that implement this
87+
// should also implement JSONPBMarshaler so that the custom format can be
88+
// produced.
89+
type JSONPBUnmarshaler interface {
90+
UnmarshalJSONPB(*Unmarshaler, []byte) error
91+
}
92+
7793
// Marshal marshals a protocol buffer into JSON.
7894
func (m *Marshaler) Marshal(out io.Writer, pb proto.Message) error {
7995
writer := &errWriter{writer: out}
@@ -102,6 +118,15 @@ type wkt interface {
102118

103119
// marshalObject writes a struct to the Writer.
104120
func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent, typeURL string) error {
121+
if jsm, ok := v.(JSONPBMarshaler); ok {
122+
b, err := jsm.MarshalJSONPB(m)
123+
if err != nil {
124+
return err
125+
}
126+
out.write(string(b))
127+
return out.err
128+
}
129+
105130
s := reflect.ValueOf(v).Elem()
106131

107132
// Handle well-known types.
@@ -571,6 +596,10 @@ func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMe
571596
return u.unmarshalValue(target.Elem(), inputValue, prop)
572597
}
573598

599+
if jsu, ok := target.Addr().Interface().(JSONPBUnmarshaler); ok {
600+
return jsu.UnmarshalJSONPB(u, []byte(inputValue))
601+
}
602+
574603
// Handle well-known types.
575604
type wkt interface {
576605
XXX_WellKnownType() string

jsonpb/jsonpb_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,18 @@ func TestMarshaling(t *testing.T) {
439439
}
440440
}
441441

442+
func TestMarshalingWithJSONPBMarshaler(t *testing.T) {
443+
rawJson := `{ "foo": "bar", "baz": [0, 1, 2, 3] }`
444+
msg := dynamicMessage{rawJson: rawJson}
445+
str, err := new(Marshaler).MarshalToString(&msg)
446+
if err != nil {
447+
t.Errorf("an unexpected error occurred when marshalling JSONPBMarshaler: %v", err)
448+
}
449+
if str != rawJson {
450+
t.Errorf("marshalling JSON produced incorrect output: got %s, wanted %s", str, rawJson)
451+
}
452+
}
453+
442454
var unmarshalingTests = []struct {
443455
desc string
444456
unmarshaler Unmarshaler
@@ -635,3 +647,41 @@ func TestUnmarshalingBadInput(t *testing.T) {
635647
}
636648
}
637649
}
650+
651+
func TestUnmarshalWithJSONPBUnmarshaler(t *testing.T) {
652+
rawJson := `{ "foo": "bar", "baz": [0, 1, 2, 3] }`
653+
var msg dynamicMessage
654+
err := Unmarshal(strings.NewReader(rawJson), &msg)
655+
if err != nil {
656+
t.Errorf("an unexpected error occurred when parsing into JSONPBUnmarshaler: %v", err)
657+
}
658+
if msg.rawJson != rawJson {
659+
t.Errorf("message contents not set correctly after unmarshalling JSON: got %s, wanted %s", msg.rawJson, rawJson)
660+
}
661+
}
662+
663+
// dynamicMessage implements protobuf.Message but is not a normal generated message type.
664+
// It provides implementations of JSONPBMarshaler and JSONPBUnmarshaler for JSON support.
665+
type dynamicMessage struct {
666+
rawJson string
667+
}
668+
669+
func (m *dynamicMessage) Reset() {
670+
m.rawJson = "{}"
671+
}
672+
673+
func (m *dynamicMessage) String() string {
674+
return m.rawJson
675+
}
676+
677+
func (m *dynamicMessage) ProtoMessage() {
678+
}
679+
680+
func (m *dynamicMessage) MarshalJSONPB(jm *Marshaler) ([]byte, error) {
681+
return []byte(m.rawJson), nil
682+
}
683+
684+
func (m *dynamicMessage) UnmarshalJSONPB(jum *Unmarshaler, json []byte) error {
685+
m.rawJson = string(json)
686+
return nil
687+
}

0 commit comments

Comments
 (0)