blob: f56072a6f15b30ed857ba9c5f9e7e5b3481a105b [file] [log] [blame]
khenaidooffe076b2019-01-15 16:08:08 -05001package runtime
2
3import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "io"
8 "reflect"
9
10 "github.com/golang/protobuf/jsonpb"
11 "github.com/golang/protobuf/proto"
12)
13
14// JSONPb is a Marshaler which marshals/unmarshals into/from JSON
15// with the "github.com/golang/protobuf/jsonpb".
16// It supports fully functionality of protobuf unlike JSONBuiltin.
17//
18// The NewDecoder method returns a DecoderWrapper, so the underlying
19// *json.Decoder methods can be used.
20type JSONPb jsonpb.Marshaler
21
22// ContentType always returns "application/json".
23func (*JSONPb) ContentType() string {
24 return "application/json"
25}
26
27// Marshal marshals "v" into JSON.
28func (j *JSONPb) Marshal(v interface{}) ([]byte, error) {
29 if _, ok := v.(proto.Message); !ok {
30 return j.marshalNonProtoField(v)
31 }
32
33 var buf bytes.Buffer
34 if err := j.marshalTo(&buf, v); err != nil {
35 return nil, err
36 }
37 return buf.Bytes(), nil
38}
39
40func (j *JSONPb) marshalTo(w io.Writer, v interface{}) error {
41 p, ok := v.(proto.Message)
42 if !ok {
43 buf, err := j.marshalNonProtoField(v)
44 if err != nil {
45 return err
46 }
47 _, err = w.Write(buf)
48 return err
49 }
50 return (*jsonpb.Marshaler)(j).Marshal(w, p)
51}
52
53// marshalNonProto marshals a non-message field of a protobuf message.
54// This function does not correctly marshals arbitrary data structure into JSON,
55// but it is only capable of marshaling non-message field values of protobuf,
56// i.e. primitive types, enums; pointers to primitives or enums; maps from
57// integer/string types to primitives/enums/pointers to messages.
58func (j *JSONPb) marshalNonProtoField(v interface{}) ([]byte, error) {
59 if v == nil {
60 return []byte("null"), nil
61 }
62 rv := reflect.ValueOf(v)
63 for rv.Kind() == reflect.Ptr {
64 if rv.IsNil() {
65 return []byte("null"), nil
66 }
67 rv = rv.Elem()
68 }
69
70 if rv.Kind() == reflect.Map {
71 m := make(map[string]*json.RawMessage)
72 for _, k := range rv.MapKeys() {
73 buf, err := j.Marshal(rv.MapIndex(k).Interface())
74 if err != nil {
75 return nil, err
76 }
77 m[fmt.Sprintf("%v", k.Interface())] = (*json.RawMessage)(&buf)
78 }
79 if j.Indent != "" {
80 return json.MarshalIndent(m, "", j.Indent)
81 }
82 return json.Marshal(m)
83 }
84 if enum, ok := rv.Interface().(protoEnum); ok && !j.EnumsAsInts {
85 return json.Marshal(enum.String())
86 }
87 return json.Marshal(rv.Interface())
88}
89
90// Unmarshal unmarshals JSON "data" into "v"
91func (j *JSONPb) Unmarshal(data []byte, v interface{}) error {
92 return unmarshalJSONPb(data, v)
93}
94
95// NewDecoder returns a Decoder which reads JSON stream from "r".
96func (j *JSONPb) NewDecoder(r io.Reader) Decoder {
97 d := json.NewDecoder(r)
98 return DecoderWrapper{Decoder: d}
99}
100
101// DecoderWrapper is a wrapper around a *json.Decoder that adds
102// support for protos to the Decode method.
103type DecoderWrapper struct {
104 *json.Decoder
105}
106
107// Decode wraps the embedded decoder's Decode method to support
108// protos using a jsonpb.Unmarshaler.
109func (d DecoderWrapper) Decode(v interface{}) error {
110 return decodeJSONPb(d.Decoder, v)
111}
112
113// NewEncoder returns an Encoder which writes JSON stream into "w".
114func (j *JSONPb) NewEncoder(w io.Writer) Encoder {
115 return EncoderFunc(func(v interface{}) error { return j.marshalTo(w, v) })
116}
117
118func unmarshalJSONPb(data []byte, v interface{}) error {
119 d := json.NewDecoder(bytes.NewReader(data))
120 return decodeJSONPb(d, v)
121}
122
123func decodeJSONPb(d *json.Decoder, v interface{}) error {
124 p, ok := v.(proto.Message)
125 if !ok {
126 return decodeNonProtoField(d, v)
127 }
128 unmarshaler := &jsonpb.Unmarshaler{AllowUnknownFields: true}
129 return unmarshaler.UnmarshalNext(d, p)
130}
131
132func decodeNonProtoField(d *json.Decoder, v interface{}) error {
133 rv := reflect.ValueOf(v)
134 if rv.Kind() != reflect.Ptr {
135 return fmt.Errorf("%T is not a pointer", v)
136 }
137 for rv.Kind() == reflect.Ptr {
138 if rv.IsNil() {
139 rv.Set(reflect.New(rv.Type().Elem()))
140 }
141 if rv.Type().ConvertibleTo(typeProtoMessage) {
142 unmarshaler := &jsonpb.Unmarshaler{AllowUnknownFields: true}
143 return unmarshaler.UnmarshalNext(d, rv.Interface().(proto.Message))
144 }
145 rv = rv.Elem()
146 }
147 if rv.Kind() == reflect.Map {
148 if rv.IsNil() {
149 rv.Set(reflect.MakeMap(rv.Type()))
150 }
151 conv, ok := convFromType[rv.Type().Key().Kind()]
152 if !ok {
153 return fmt.Errorf("unsupported type of map field key: %v", rv.Type().Key())
154 }
155
156 m := make(map[string]*json.RawMessage)
157 if err := d.Decode(&m); err != nil {
158 return err
159 }
160 for k, v := range m {
161 result := conv.Call([]reflect.Value{reflect.ValueOf(k)})
162 if err := result[1].Interface(); err != nil {
163 return err.(error)
164 }
165 bk := result[0]
166 bv := reflect.New(rv.Type().Elem())
167 if err := unmarshalJSONPb([]byte(*v), bv.Interface()); err != nil {
168 return err
169 }
170 rv.SetMapIndex(bk, bv.Elem())
171 }
172 return nil
173 }
174 if _, ok := rv.Interface().(protoEnum); ok {
175 var repr interface{}
176 if err := d.Decode(&repr); err != nil {
177 return err
178 }
179 switch repr.(type) {
180 case string:
181 // TODO(yugui) Should use proto.StructProperties?
182 return fmt.Errorf("unmarshaling of symbolic enum %q not supported: %T", repr, rv.Interface())
183 case float64:
184 rv.Set(reflect.ValueOf(int32(repr.(float64))).Convert(rv.Type()))
185 return nil
186 default:
187 return fmt.Errorf("cannot assign %#v into Go type %T", repr, rv.Interface())
188 }
189 }
190 return d.Decode(v)
191}
192
193type protoEnum interface {
194 fmt.Stringer
195 EnumDescriptor() ([]byte, []int)
196}
197
198var typeProtoMessage = reflect.TypeOf((*proto.Message)(nil)).Elem()
199
200// Delimiter for newline encoded JSON streams.
201func (j *JSONPb) Delimiter() []byte {
202 return []byte("\n")
203}