blob: ef8dff5def089ebef9b864f2377363d086e124b9 [file] [log] [blame]
Buck Clay67cbcad2015-07-01 11:17:11 +10001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2015 The Go Authors. All rights reserved.
4// https://github.com/golang/protobuf
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32/*
33Package jsonpb provides marshalling/unmarshalling functionality between
34protocol buffer and JSON objects.
35
36Compared to encoding/json, this library:
37 - encodes int64, uint64 as strings
38 - optionally encodes enums as strings
39*/
40package jsonpb
41
42import (
43 "bytes"
44 "encoding/json"
45 "fmt"
46 "io"
47 "reflect"
48 "sort"
49 "strconv"
50 "strings"
51
52 "github.com/golang/protobuf/proto"
53)
54
55var (
56 byteArrayType = reflect.TypeOf([]byte{})
57)
58
59// Marshaller is a configurable object for converting between
60// protocol buffer objects and a JSON representation for them
61type Marshaller struct {
62 // Use string values for enums (as opposed to integer values)
63 EnumsAsString bool
64
65 // A string to indent each level by. The presence of this field will
66 // also cause a space to appear between the field separator and
67 // value, and for newlines to be appear between fields and array
68 // elements.
69 Indent string
70}
71
72// Marshal marshals a protocol buffer into JSON.
73func (m *Marshaller) Marshal(out io.Writer, pb proto.Message) error {
74 writer := &errWriter{writer: out}
75 return m.marshalObject(writer, pb, "")
76}
77
78// MarshalToString converts a protocol buffer object to JSON string.
79func (m *Marshaller) MarshalToString(pb proto.Message) (string, error) {
80 var buf bytes.Buffer
81 if err := m.Marshal(&buf, pb); err != nil {
82 return "", err
83 }
84 return buf.String(), nil
85}
86
87// marshalObject writes a struct to the Writer.
88func (m *Marshaller) marshalObject(out *errWriter, v proto.Message, indent string) error {
89 out.write("{")
90 if m.Indent != "" {
91 out.write("\n")
92 }
93
94 s := reflect.ValueOf(v).Elem()
95 writeBeforeField := ""
96 for i := 0; i < s.NumField(); i++ {
97 value := s.Field(i)
98 valueField := s.Type().Field(i)
David Symonds31db5692015-08-10 12:45:00 +100099 if strings.HasPrefix(valueField.Name, "XXX_") {
Buck Clay67cbcad2015-07-01 11:17:11 +1000100 continue
David Symonds31db5692015-08-10 12:45:00 +1000101 }
102 fieldName := jsonFieldName(valueField)
103
104 // TODO: proto3 objects should have default values omitted.
105
106 // IsNil will panic on most value kinds.
107 switch value.Kind() {
108 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
109 if value.IsNil() {
Buck Clay67cbcad2015-07-01 11:17:11 +1000110 continue
111 }
112 }
113
114 out.write(writeBeforeField)
115 if m.Indent != "" {
116 out.write(indent)
117 out.write(m.Indent)
118 }
119 out.write(`"`)
120 out.write(fieldName)
121 out.write(`":`)
122 if m.Indent != "" {
123 out.write(" ")
124 }
125
126 if err := m.marshalValue(out, value, valueField, indent); err != nil {
127 return err
128 }
129
130 if m.Indent != "" {
131 writeBeforeField = ",\n"
132 } else {
133 writeBeforeField = ","
134 }
135 }
136
137 if m.Indent != "" {
138 out.write("\n")
139 out.write(indent)
140 }
141 out.write("}")
142 return out.err
143}
144
145// marshalValue writes the value to the Writer.
146func (m *Marshaller) marshalValue(out *errWriter, v reflect.Value,
147 structField reflect.StructField, indent string) error {
148
149 var err error
150 v = reflect.Indirect(v)
151
152 // Handle repeated elements.
153 if v.Type() != byteArrayType && v.Kind() == reflect.Slice {
154 out.write("[")
155 comma := ""
156 for i := 0; i < v.Len(); i++ {
157 sliceVal := v.Index(i)
158 out.write(comma)
159 if m.Indent != "" {
160 out.write("\n")
161 out.write(indent)
162 out.write(m.Indent)
163 out.write(m.Indent)
164 }
165 m.marshalValue(out, sliceVal, structField, indent+m.Indent)
166 comma = ","
167 }
168 if m.Indent != "" {
169 out.write("\n")
170 out.write(indent)
171 out.write(m.Indent)
172 }
173 out.write("]")
174 return out.err
175 }
176
177 // Handle enumerations.
178 protoInfo := structField.Tag.Get("protobuf")
179 if m.EnumsAsString && strings.Contains(protoInfo, ",enum=") {
180 // Unknown enum values will are stringified by the proto library as their
181 // value. Such values should _not_ be quoted or they will be intrepreted
182 // as an enum string instead of their value.
183 enumStr := v.Interface().(fmt.Stringer).String()
184 var valStr string
185 if v.Kind() == reflect.Ptr {
186 valStr = strconv.Itoa(int(v.Elem().Int()))
187 } else {
188 valStr = strconv.Itoa(int(v.Int()))
189 }
190 isKnownEnum := enumStr != valStr
191 if isKnownEnum {
192 out.write(`"`)
193 }
194 out.write(enumStr)
195 if isKnownEnum {
196 out.write(`"`)
197 }
198 return out.err
199 }
200
201 // Handle nested messages.
202 if v.Kind() == reflect.Struct {
203 return m.marshalObject(out, v.Addr().Interface().(proto.Message), indent+m.Indent)
204 }
205
206 // Handle maps.
David Symonds3fe63ce2015-08-07 15:00:00 +1000207 // Since Go randomizes map iteration, we sort keys for stable output.
Buck Clay67cbcad2015-07-01 11:17:11 +1000208 if v.Kind() == reflect.Map {
209 out.write(`{`)
210 keys := v.MapKeys()
211 sort.Sort(mapKeys(keys))
212 for i, k := range keys {
213 if i > 0 {
214 out.write(`,`)
215 }
216 if m.Indent != "" {
217 out.write("\n")
218 out.write(indent)
219 out.write(m.Indent)
220 out.write(m.Indent)
221 }
222
223 b, err := json.Marshal(k.Interface())
224 if err != nil {
225 return err
226 }
227 s := string(b)
228
229 // If the JSON is not a string value, encode it again to make it one.
230 if !strings.HasPrefix(s, `"`) {
231 b, err := json.Marshal(s)
232 if err != nil {
233 return err
234 }
235 s = string(b)
236 }
237
238 out.write(s)
239 out.write(`:`)
240 if m.Indent != "" {
241 out.write(` `)
242 }
243
244 if err := m.marshalValue(out, v.MapIndex(k), structField, indent+m.Indent); err != nil {
245 return err
246 }
247 }
248 if m.Indent != "" {
249 out.write("\n")
250 out.write(indent)
251 out.write(m.Indent)
252 }
253 out.write(`}`)
254 return out.err
255 }
256
257 // Default handling defers to the encoding/json library.
258 b, err := json.Marshal(v.Interface())
259 if err != nil {
260 return err
261 }
262 needToQuote := string(b[0]) != `"` && (v.Kind() == reflect.Int64 || v.Kind() == reflect.Uint64)
263 if needToQuote {
264 out.write(`"`)
265 }
266 out.write(string(b))
267 if needToQuote {
268 out.write(`"`)
269 }
270 return out.err
271}
272
273// Unmarshal unmarshals a JSON object stream into a protocol
274// buffer. This function is lenient and will decode any options
275// permutations of the related Marshaller.
276func Unmarshal(r io.Reader, pb proto.Message) error {
277 inputValue := json.RawMessage{}
278 if err := json.NewDecoder(r).Decode(&inputValue); err != nil {
279 return err
280 }
281 return unmarshalValue(reflect.ValueOf(pb).Elem(), inputValue)
282}
283
284// UnmarshalString will populate the fields of a protocol buffer based
285// on a JSON string. This function is lenient and will decode any options
286// permutations of the related Marshaller.
287func UnmarshalString(str string, pb proto.Message) error {
288 return Unmarshal(bytes.NewReader([]byte(str)), pb)
289}
290
291// unmarshalValue converts/copies a value into the target.
292func unmarshalValue(target reflect.Value, inputValue json.RawMessage) error {
293 targetType := target.Type()
294
295 // Allocate memory for pointer fields.
296 if targetType.Kind() == reflect.Ptr {
297 target.Set(reflect.New(targetType.Elem()))
298 return unmarshalValue(target.Elem(), inputValue)
299 }
300
301 // Handle nested messages.
302 if targetType.Kind() == reflect.Struct {
303 var jsonFields map[string]json.RawMessage
304 if err := json.Unmarshal(inputValue, &jsonFields); err != nil {
305 return err
306 }
307
308 for i := 0; i < target.NumField(); i++ {
David Symonds31db5692015-08-10 12:45:00 +1000309 ft := target.Type().Field(i)
310 if strings.HasPrefix(ft.Name, "XXX_") {
Buck Clay67cbcad2015-07-01 11:17:11 +1000311 continue
312 }
David Symonds31db5692015-08-10 12:45:00 +1000313 fieldName := jsonFieldName(ft)
Buck Clay67cbcad2015-07-01 11:17:11 +1000314
315 if valueForField, ok := jsonFields[fieldName]; ok {
316 if err := unmarshalValue(target.Field(i), valueForField); err != nil {
317 return err
318 }
David Symonds3fe63ce2015-08-07 15:00:00 +1000319 delete(jsonFields, fieldName)
Buck Clay67cbcad2015-07-01 11:17:11 +1000320 }
321 }
David Symonds3fe63ce2015-08-07 15:00:00 +1000322 if len(jsonFields) > 0 {
323 // Pick any field to be the scapegoat.
324 var f string
325 for fname := range jsonFields {
326 f = fname
327 break
328 }
329 return fmt.Errorf("unknown field %q in %v", f, targetType)
330 }
Buck Clay67cbcad2015-07-01 11:17:11 +1000331 return nil
332 }
333
334 // Handle arrays (which aren't encoded bytes)
335 if targetType != byteArrayType && targetType.Kind() == reflect.Slice {
336 var slc []json.RawMessage
337 if err := json.Unmarshal(inputValue, &slc); err != nil {
338 return err
339 }
340 len := len(slc)
341 target.Set(reflect.MakeSlice(targetType, len, len))
342 for i := 0; i < len; i++ {
343 if err := unmarshalValue(target.Index(i), slc[i]); err != nil {
344 return err
345 }
346 }
347 return nil
348 }
349
350 // Handle maps (whose keys are always strings)
351 if targetType.Kind() == reflect.Map {
352 var mp map[string]json.RawMessage
353 if err := json.Unmarshal(inputValue, &mp); err != nil {
354 return err
355 }
356 target.Set(reflect.MakeMap(targetType))
357 for ks, raw := range mp {
358 // Unmarshal map key. The core json library already decoded the key into a
359 // string, so we handle that specially. Other types were quoted post-serialization.
360 var k reflect.Value
361 if targetType.Key().Kind() == reflect.String {
362 k = reflect.ValueOf(ks)
363 } else {
364 k = reflect.New(targetType.Key()).Elem()
365 if err := unmarshalValue(k, json.RawMessage(ks)); err != nil {
366 return err
367 }
368 }
369
370 // Unmarshal map value.
371 v := reflect.New(targetType.Elem()).Elem()
372 if err := unmarshalValue(v, raw); err != nil {
373 return err
374 }
375 target.SetMapIndex(k, v)
376 }
377 return nil
378 }
379
380 // 64-bit integers can be encoded as strings. In this case we drop
381 // the quotes and proceed as normal.
382 isNum := targetType.Kind() == reflect.Int64 || targetType.Kind() == reflect.Uint64
383 if isNum && strings.HasPrefix(string(inputValue), `"`) {
384 inputValue = inputValue[1 : len(inputValue)-1]
385 }
386
387 // Use the encoding/json for parsing other value types.
388 return json.Unmarshal(inputValue, target.Addr().Interface())
389}
390
391// hasUnmarshalJSON is a interface implemented by protocol buffer enums.
392type hasUnmarshalJSON interface {
393 UnmarshalJSON(data []byte) error
394}
395
David Symonds31db5692015-08-10 12:45:00 +1000396// jsonFieldName returns the field name to use.
397func jsonFieldName(f reflect.StructField) string {
398 var prop proto.Properties
399 prop.Init(f.Type, f.Name, f.Tag.Get("protobuf"), &f)
400 return prop.OrigName
Buck Clay67cbcad2015-07-01 11:17:11 +1000401}
402
403// Writer wrapper inspired by https://blog.golang.org/errors-are-values
404type errWriter struct {
405 writer io.Writer
406 err error
407}
408
409func (w *errWriter) write(str string) {
410 if w.err != nil {
411 return
412 }
413 _, w.err = w.writer.Write([]byte(str))
414}
415
416// Map fields may have key types of non-float scalars, strings and enums.
417// The easiest way to sort them in some deterministic order is to use fmt.
418// If this turns out to be inefficient we can always consider other options,
419// such as doing a Schwartzian transform.
420type mapKeys []reflect.Value
421
422func (s mapKeys) Len() int { return len(s) }
423func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
424func (s mapKeys) Less(i, j int) bool {
425 return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface())
426}