@@ -3,8 +3,9 @@ package runtime
33import (
44 "bytes"
55 "encoding/json"
6- "errors "
6+ "fmt "
77 "io"
8+ "reflect"
89
910 "github.com/golang/protobuf/jsonpb"
1011 "github.com/golang/protobuf/proto"
@@ -39,79 +40,159 @@ func (*JSONPb) ContentType() string {
3940// Currently it can marshal only proto.Message.
4041// TODO(yugui) Support fields of primitive types in a message.
4142func (j * JSONPb ) Marshal (v interface {}) ([]byte , error ) {
43+ if _ , ok := v .(proto.Message ); ! ok {
44+ return j .marshalNonProtoField (v )
45+ }
46+
47+ var buf bytes.Buffer
48+ if err := j .marshalTo (& buf , v ); err != nil {
49+ return nil , err
50+ }
51+ return buf .Bytes (), nil
52+ }
53+
54+ func (j * JSONPb ) marshalTo (w io.Writer , v interface {}) error {
55+ p , ok := v .(proto.Message )
56+ if ! ok {
57+ buf , err := j .marshalNonProtoField (v )
58+ if err != nil {
59+ return err
60+ }
61+ _ , err = w .Write (buf )
62+ return err
63+ }
4264 m := & jsonpb.Marshaler {
4365 EnumsAsInts : j .EnumsAsInts ,
4466 EmitDefaults : j .EmitDefaults ,
4567 Indent : j .Indent ,
4668 OrigName : j .OrigName ,
4769 }
48- p , ok := v .(proto.Message )
49- if ! ok {
50- return nil , errors .New ("interface is not proto.Message" )
51- }
70+ return m .Marshal (w , p )
71+ }
5272
53- var buf bytes.Buffer
54- if err := m .Marshal (& buf , p ); err != nil {
55- return nil , err
73+ // marshalNonProto marshals a non-message field of a protobuf message.
74+ // This function does not correctly marshals arbitary data structure into JSON,
75+ // but it is only capable of marshaling non-message field values of protobuf,
76+ // i.e. primitive types, enums; pointers to primitives or enums; maps from
77+ // integer/string types to primitives/enums/pointers to messages.
78+ func (j * JSONPb ) marshalNonProtoField (v interface {}) ([]byte , error ) {
79+ rv := reflect .ValueOf (v )
80+ for rv .Kind () == reflect .Ptr {
81+ if rv .IsNil () {
82+ return []byte ("null" ), nil
83+ }
84+ rv = rv .Elem ()
5685 }
57- return buf .Bytes (), nil
5886
87+ if rv .Kind () == reflect .Map {
88+ m := make (map [string ]* json.RawMessage )
89+ for _ , k := range rv .MapKeys () {
90+ buf , err := j .Marshal (rv .MapIndex (k ).Interface ())
91+ if err != nil {
92+ return nil , err
93+ }
94+ m [fmt .Sprintf ("%v" , k .Interface ())] = (* json .RawMessage )(& buf )
95+ }
96+ if j .Indent != "" {
97+ return json .MarshalIndent (m , "" , j .Indent )
98+ }
99+ return json .Marshal (m )
100+ }
101+ if enum , ok := rv .Interface ().(protoEnum ); ok && ! j .EnumsAsInts {
102+ return json .Marshal (enum .String ())
103+ }
104+ return json .Marshal (rv .Interface ())
59105}
60106
61107// Unmarshal unmarshals JSON "data" into "v"
62108// Currently it can marshal only proto.Message.
63109// TODO(yugui) Support fields of primitive types in a message.
64110func (j * JSONPb ) Unmarshal (data []byte , v interface {}) error {
65- r := bytes .NewReader (data )
66- p , ok := v .(proto.Message )
67- if ! ok {
68- return errors .New ("interface is not proto.Message" )
69- }
70- return jsonpb .Unmarshal (r , p )
111+ return unmarshalJSONPb (data , v )
71112}
72113
73114// NewDecoder returns a Decoder which reads JSON stream from "r".
74115func (j * JSONPb ) NewDecoder (r io.Reader ) Decoder {
75- return & jsonPbDecoder {decoder : json .NewDecoder (r )}
116+ d := json .NewDecoder (r )
117+ return DecoderFunc (func (v interface {}) error { return decodeJSONPb (d , v ) })
76118}
77119
78120// NewEncoder returns an Encoder which writes JSON stream into "w".
79121func (j * JSONPb ) NewEncoder (w io.Writer ) Encoder {
80- m := & jsonpb.Marshaler {
81- EnumsAsInts : j .EnumsAsInts ,
82- EmitDefaults : j .EmitDefaults ,
83- Indent : j .Indent ,
84- OrigName : j .OrigName ,
85- }
86-
87- return & jsonPbEncoder {
88- marshal : m ,
89- writer : w ,
90- }
122+ return EncoderFunc (func (v interface {}) error { return j .marshalTo (w , v ) })
91123}
92124
93- type jsonPbDecoder struct {
94- decoder * json.Decoder
125+ func unmarshalJSONPb (data []byte , v interface {}) error {
126+ d := json .NewDecoder (bytes .NewReader (data ))
127+ return decodeJSONPb (d , v )
95128}
96129
97- func ( j * jsonPbDecoder ) Decode ( v interface {}) error {
130+ func decodeJSONPb ( d * json. Decoder , v interface {}) error {
98131 p , ok := v .(proto.Message )
99132 if ! ok {
100- return errors . New ( "interface is not proto.Message" )
133+ return decodeNonProtoField ( d , v )
101134 }
102-
103- return jsonpb .UnmarshalNext (j .decoder , p )
135+ return jsonpb .UnmarshalNext (d , p )
104136}
105137
106- type jsonPbEncoder struct {
107- marshal * jsonpb.Marshaler
108- writer io.Writer
138+ func decodeNonProtoField (d * json.Decoder , v interface {}) error {
139+ rv := reflect .ValueOf (v )
140+ if rv .Kind () != reflect .Ptr {
141+ return fmt .Errorf ("%T is not a pointer" , v )
142+ }
143+ for rv .Kind () == reflect .Ptr {
144+ if rv .IsNil () {
145+ rv .Set (reflect .New (rv .Type ().Elem ()))
146+ }
147+ rv = rv .Elem ()
148+ }
149+ if rv .Kind () == reflect .Map {
150+ if rv .IsNil () {
151+ rv .Set (reflect .MakeMap (rv .Type ()))
152+ }
153+ conv , ok := convFromType [rv .Type ().Key ().Kind ()]
154+ if ! ok {
155+ return fmt .Errorf ("unsupported type of map field key: %v" , rv .Type ().Key ())
156+ }
157+
158+ m := make (map [string ]* json.RawMessage )
159+ if err := d .Decode (& m ); err != nil {
160+ return err
161+ }
162+ for k , v := range m {
163+ result := conv .Call ([]reflect.Value {reflect .ValueOf (k )})
164+ if err := result [1 ].Interface (); err != nil {
165+ return err .(error )
166+ }
167+ bk := result [0 ]
168+ bv := reflect .New (rv .Type ().Elem ())
169+ if err := unmarshalJSONPb ([]byte (* v ), bv .Interface ()); err != nil {
170+ return err
171+ }
172+ rv .SetMapIndex (bk , bv .Elem ())
173+ }
174+ return nil
175+ }
176+ if _ , ok := rv .Interface ().(protoEnum ); ok {
177+ var repr interface {}
178+ if err := d .Decode (& repr ); err != nil {
179+ return err
180+ }
181+ switch repr .(type ) {
182+ case string :
183+ // TODO(yugui) Should use proto.StructProperties?
184+ return fmt .Errorf ("unmarshaling of symbolic enum %q not supported: %T" , repr , rv .Interface ())
185+ case float64 :
186+ rv .Set (reflect .ValueOf (int32 (repr .(float64 ))).Convert (rv .Type ()))
187+ return nil
188+ default :
189+ return fmt .Errorf ("cannot assign %#v into Go type %T" , repr , rv .Interface ())
190+ }
191+ }
192+ return d .Decode (v )
109193}
110194
111- func (j * jsonPbEncoder ) Encode (v interface {}) error {
112- p , ok := v .(proto.Message )
113- if ! ok {
114- return errors .New ("interface is not proto.Message" )
115- }
116- return j .marshal .Marshal (j .writer , p )
195+ type protoEnum interface {
196+ fmt.Stringer
197+ EnumDescriptor () ([]byte , []int )
117198}
0 commit comments