blob: 71985e64ff88debba2eaaf70f3d07e75865c2177 [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/*
David Symonds5fc22942016-01-21 14:29:00 +110033Package jsonpb provides marshaling and unmarshaling between protocol buffers and JSON.
34It follows the specification at https://developers.google.com/protocol-buffers/docs/proto3#json.
Buck Clay67cbcad2015-07-01 11:17:11 +100035
David Symonds5fc22942016-01-21 14:29:00 +110036This package produces a different output than the standard "encoding/json" package,
37which does not operate correctly on protocol buffers.
Buck Clay67cbcad2015-07-01 11:17:11 +100038*/
39package jsonpb
40
41import (
42 "bytes"
43 "encoding/json"
David Symonds552c7b92016-02-26 12:45:00 +110044 "errors"
Buck Clay67cbcad2015-07-01 11:17:11 +100045 "fmt"
46 "io"
47 "reflect"
48 "sort"
49 "strconv"
50 "strings"
David Symonds553c7642016-02-23 14:14:00 +110051 "time"
Buck Clay67cbcad2015-07-01 11:17:11 +100052
53 "github.com/golang/protobuf/proto"
54)
55
56var (
57 byteArrayType = reflect.TypeOf([]byte{})
58)
59
David Symonds21f81362015-08-17 11:59:00 +100060// Marshaler is a configurable object for converting between
David Symonds45bba202016-01-29 08:30:00 +110061// protocol buffer objects and a JSON representation for them.
David Symonds21f81362015-08-17 11:59:00 +100062type Marshaler struct {
David Symonds0ea3c032015-10-19 13:53:00 +110063 // Whether to render enum values as integers, as opposed to string values.
64 EnumsAsInts bool
65
David Symonds45bba202016-01-29 08:30:00 +110066 // Whether to render fields with zero values.
67 EmitDefaults bool
68
Buck Clay67cbcad2015-07-01 11:17:11 +100069 // A string to indent each level by. The presence of this field will
70 // also cause a space to appear between the field separator and
71 // value, and for newlines to be appear between fields and array
72 // elements.
73 Indent string
David Symonds001690d2016-02-10 14:04:11 +110074
75 // Whether to use the original (.proto) name for fields.
76 OrigName bool
Buck Clay67cbcad2015-07-01 11:17:11 +100077}
78
79// Marshal marshals a protocol buffer into JSON.
David Symonds21f81362015-08-17 11:59:00 +100080func (m *Marshaler) Marshal(out io.Writer, pb proto.Message) error {
Buck Clay67cbcad2015-07-01 11:17:11 +100081 writer := &errWriter{writer: out}
David Symondsbf531ff2016-04-20 14:40:00 +100082 return m.marshalObject(writer, pb, "", "")
Buck Clay67cbcad2015-07-01 11:17:11 +100083}
84
85// MarshalToString converts a protocol buffer object to JSON string.
David Symonds21f81362015-08-17 11:59:00 +100086func (m *Marshaler) MarshalToString(pb proto.Message) (string, error) {
Buck Clay67cbcad2015-07-01 11:17:11 +100087 var buf bytes.Buffer
88 if err := m.Marshal(&buf, pb); err != nil {
89 return "", err
90 }
91 return buf.String(), nil
92}
93
Juraj Stachof9dd6932015-11-03 18:47:00 +110094type int32Slice []int32
95
96// For sorting extensions ids to ensure stable output.
97func (s int32Slice) Len() int { return len(s) }
98func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
99func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
100
David Symondsbf531ff2016-04-20 14:40:00 +1000101type wkt interface {
102 XXX_WellKnownType() string
103}
104
Buck Clay67cbcad2015-07-01 11:17:11 +1000105// marshalObject writes a struct to the Writer.
David Symondsbf531ff2016-04-20 14:40:00 +1000106func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent, typeURL string) error {
David Symonds553c7642016-02-23 14:14:00 +1100107 s := reflect.ValueOf(v).Elem()
108
109 // Handle well-known types.
David Symonds553c7642016-02-23 14:14:00 +1100110 if wkt, ok := v.(wkt); ok {
111 switch wkt.XXX_WellKnownType() {
David Symondsc6184d32016-02-25 11:12:00 +1100112 case "DoubleValue", "FloatValue", "Int64Value", "UInt64Value",
113 "Int32Value", "UInt32Value", "BoolValue", "StringValue", "BytesValue":
114 // "Wrappers use the same representation in JSON
115 // as the wrapped primitive type, ..."
116 sprop := proto.GetProperties(s.Type())
117 return m.marshalValue(out, sprop.Prop[0], s.Field(0), indent)
David Symondsbf531ff2016-04-20 14:40:00 +1000118 case "Any":
119 // Any is a bit more involved.
120 return m.marshalAny(out, v, indent)
David Symonds553c7642016-02-23 14:14:00 +1100121 case "Duration":
122 // "Generated output always contains 3, 6, or 9 fractional digits,
123 // depending on required precision."
124 s, ns := s.Field(0).Int(), s.Field(1).Int()
125 d := time.Duration(s)*time.Second + time.Duration(ns)*time.Nanosecond
126 x := fmt.Sprintf("%.9f", d.Seconds())
127 x = strings.TrimSuffix(x, "000")
128 x = strings.TrimSuffix(x, "000")
129 out.write(`"`)
130 out.write(x)
131 out.write(`s"`)
132 return out.err
David Symonds552c7b92016-02-26 12:45:00 +1100133 case "Struct":
134 // Let marshalValue handle the `fields` map.
135 // TODO: pass the correct Properties if needed.
136 return m.marshalValue(out, &proto.Properties{}, s.Field(0), indent)
David Symonds553c7642016-02-23 14:14:00 +1100137 case "Timestamp":
138 // "RFC 3339, where generated output will always be Z-normalized
139 // and uses 3, 6 or 9 fractional digits."
140 s, ns := s.Field(0).Int(), s.Field(1).Int()
141 t := time.Unix(s, ns).UTC()
142 // time.RFC3339Nano isn't exactly right (we need to get 3/6/9 fractional digits).
143 x := t.Format("2006-01-02T15:04:05.000000000")
144 x = strings.TrimSuffix(x, "000")
145 x = strings.TrimSuffix(x, "000")
146 out.write(`"`)
147 out.write(x)
148 out.write(`Z"`)
149 return out.err
David Symonds552c7b92016-02-26 12:45:00 +1100150 case "Value":
151 // Value has a single oneof.
152 kind := s.Field(0)
153 if kind.IsNil() {
154 // "absence of any variant indicates an error"
155 return errors.New("nil Value")
156 }
157 // oneof -> *T -> T -> T.F
158 x := kind.Elem().Elem().Field(0)
159 // TODO: pass the correct Properties if needed.
160 return m.marshalValue(out, &proto.Properties{}, x, indent)
David Symonds553c7642016-02-23 14:14:00 +1100161 }
162 }
163
Buck Clay67cbcad2015-07-01 11:17:11 +1000164 out.write("{")
165 if m.Indent != "" {
166 out.write("\n")
167 }
168
Juraj Stachof9dd6932015-11-03 18:47:00 +1100169 firstField := true
David Symondsbf531ff2016-04-20 14:40:00 +1000170
171 if typeURL != "" {
172 if err := m.marshalTypeURL(out, indent, typeURL); err != nil {
173 return err
174 }
175 firstField = false
176 }
177
Buck Clay67cbcad2015-07-01 11:17:11 +1000178 for i := 0; i < s.NumField(); i++ {
179 value := s.Field(i)
180 valueField := s.Type().Field(i)
David Symonds31db5692015-08-10 12:45:00 +1000181 if strings.HasPrefix(valueField.Name, "XXX_") {
Buck Clay67cbcad2015-07-01 11:17:11 +1000182 continue
David Symonds31db5692015-08-10 12:45:00 +1000183 }
David Symonds31db5692015-08-10 12:45:00 +1000184
David Symonds31db5692015-08-10 12:45:00 +1000185 // IsNil will panic on most value kinds.
186 switch value.Kind() {
187 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
188 if value.IsNil() {
Buck Clay67cbcad2015-07-01 11:17:11 +1000189 continue
190 }
191 }
192
David Symonds45bba202016-01-29 08:30:00 +1100193 if !m.EmitDefaults {
194 switch value.Kind() {
195 case reflect.Bool:
196 if !value.Bool() {
197 continue
198 }
199 case reflect.Int32, reflect.Int64:
200 if value.Int() == 0 {
201 continue
202 }
203 case reflect.Uint32, reflect.Uint64:
204 if value.Uint() == 0 {
205 continue
206 }
207 case reflect.Float32, reflect.Float64:
208 if value.Float() == 0 {
209 continue
210 }
211 case reflect.String:
212 if value.Len() == 0 {
213 continue
214 }
215 }
216 }
217
David Symonds59b73b32015-08-24 13:22:02 +1000218 // Oneof fields need special handling.
219 if valueField.Tag.Get("protobuf_oneof") != "" {
220 // value is an interface containing &T{real_value}.
221 sv := value.Elem().Elem() // interface -> *T -> T
222 value = sv.Field(0)
223 valueField = sv.Type().Field(0)
David Symonds59b73b32015-08-24 13:22:02 +1000224 }
David Symonds001690d2016-02-10 14:04:11 +1100225 prop := jsonProperties(valueField, m.OrigName)
Juraj Stachof9dd6932015-11-03 18:47:00 +1100226 if !firstField {
227 m.writeSep(out)
Buck Clay67cbcad2015-07-01 11:17:11 +1000228 }
Juraj Stachof9dd6932015-11-03 18:47:00 +1100229 if err := m.marshalField(out, prop, value, indent); err != nil {
Buck Clay67cbcad2015-07-01 11:17:11 +1000230 return err
231 }
Juraj Stachof9dd6932015-11-03 18:47:00 +1100232 firstField = false
233 }
Buck Clay67cbcad2015-07-01 11:17:11 +1000234
Juraj Stachof9dd6932015-11-03 18:47:00 +1100235 // Handle proto2 extensions.
matloob@google.come51d0022016-05-23 09:09:04 -0400236 if ep, ok := v.(proto.Message); ok {
Juraj Stachof9dd6932015-11-03 18:47:00 +1100237 extensions := proto.RegisteredExtensions(v)
Juraj Stachof9dd6932015-11-03 18:47:00 +1100238 // Sort extensions for stable output.
matloob@google.come51d0022016-05-23 09:09:04 -0400239 ids := make([]int32, 0, len(extensions))
240 for id, desc := range extensions {
241 if !proto.HasExtension(ep, desc) {
242 continue
243 }
Juraj Stachof9dd6932015-11-03 18:47:00 +1100244 ids = append(ids, id)
Buck Clay67cbcad2015-07-01 11:17:11 +1000245 }
Juraj Stachof9dd6932015-11-03 18:47:00 +1100246 sort.Sort(int32Slice(ids))
247 for _, id := range ids {
248 desc := extensions[id]
249 if desc == nil {
250 // unknown extension
251 continue
252 }
253 ext, extErr := proto.GetExtension(ep, desc)
254 if extErr != nil {
255 return extErr
256 }
257 value := reflect.ValueOf(ext)
258 var prop proto.Properties
259 prop.Parse(desc.Tag)
David Symonds001690d2016-02-10 14:04:11 +1100260 prop.JSONName = fmt.Sprintf("[%s]", desc.Name)
Juraj Stachof9dd6932015-11-03 18:47:00 +1100261 if !firstField {
262 m.writeSep(out)
263 }
264 if err := m.marshalField(out, &prop, value, indent); err != nil {
265 return err
266 }
267 firstField = false
268 }
269
Buck Clay67cbcad2015-07-01 11:17:11 +1000270 }
271
272 if m.Indent != "" {
273 out.write("\n")
274 out.write(indent)
275 }
276 out.write("}")
277 return out.err
278}
279
Juraj Stachof9dd6932015-11-03 18:47:00 +1100280func (m *Marshaler) writeSep(out *errWriter) {
281 if m.Indent != "" {
282 out.write(",\n")
283 } else {
284 out.write(",")
285 }
286}
287
David Symondsbf531ff2016-04-20 14:40:00 +1000288func (m *Marshaler) marshalAny(out *errWriter, any proto.Message, indent string) error {
289 // "If the Any contains a value that has a special JSON mapping,
290 // it will be converted as follows: {"@type": xxx, "value": yyy}.
291 // Otherwise, the value will be converted into a JSON object,
292 // and the "@type" field will be inserted to indicate the actual data type."
293 v := reflect.ValueOf(any).Elem()
294 turl := v.Field(0).String()
295 val := v.Field(1).Bytes()
296
297 // Only the part of type_url after the last slash is relevant.
298 mname := turl
299 if slash := strings.LastIndex(mname, "/"); slash >= 0 {
300 mname = mname[slash+1:]
301 }
302 mt := proto.MessageType(mname)
303 if mt == nil {
304 return fmt.Errorf("unknown message type %q", mname)
305 }
306 msg := reflect.New(mt.Elem()).Interface().(proto.Message)
307 if err := proto.Unmarshal(val, msg); err != nil {
308 return err
309 }
310
311 if _, ok := msg.(wkt); ok {
312 out.write("{")
313 if m.Indent != "" {
314 out.write("\n")
315 }
316 if err := m.marshalTypeURL(out, indent, turl); err != nil {
317 return err
318 }
319 m.writeSep(out)
320 out.write(`"value":`)
321 if err := m.marshalObject(out, msg, indent, ""); err != nil {
322 return err
323 }
324 if m.Indent != "" {
325 out.write("\n")
326 out.write(indent)
327 }
328 out.write("}")
329 return out.err
330 }
331
332 return m.marshalObject(out, msg, indent, turl)
333}
334
335func (m *Marshaler) marshalTypeURL(out *errWriter, indent, typeURL string) error {
336 if m.Indent != "" {
337 out.write(indent)
338 out.write(m.Indent)
339 }
340 out.write(`"@type":`)
341 if m.Indent != "" {
342 out.write(" ")
343 }
344 b, err := json.Marshal(typeURL)
345 if err != nil {
346 return err
347 }
348 out.write(string(b))
349 return out.err
350}
351
Juraj Stachof9dd6932015-11-03 18:47:00 +1100352// marshalField writes field description and value to the Writer.
353func (m *Marshaler) marshalField(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error {
354 if m.Indent != "" {
355 out.write(indent)
356 out.write(m.Indent)
357 }
358 out.write(`"`)
David Symonds001690d2016-02-10 14:04:11 +1100359 out.write(prop.JSONName)
Juraj Stachof9dd6932015-11-03 18:47:00 +1100360 out.write(`":`)
361 if m.Indent != "" {
362 out.write(" ")
363 }
364 if err := m.marshalValue(out, prop, v, indent); err != nil {
365 return err
366 }
367 return nil
368}
369
Buck Clay67cbcad2015-07-01 11:17:11 +1000370// marshalValue writes the value to the Writer.
Juraj Stachof9dd6932015-11-03 18:47:00 +1100371func (m *Marshaler) marshalValue(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error {
Buck Clay67cbcad2015-07-01 11:17:11 +1000372
373 var err error
374 v = reflect.Indirect(v)
375
376 // Handle repeated elements.
377 if v.Type() != byteArrayType && v.Kind() == reflect.Slice {
378 out.write("[")
379 comma := ""
380 for i := 0; i < v.Len(); i++ {
381 sliceVal := v.Index(i)
382 out.write(comma)
383 if m.Indent != "" {
384 out.write("\n")
385 out.write(indent)
386 out.write(m.Indent)
387 out.write(m.Indent)
388 }
Juraj Stachof9dd6932015-11-03 18:47:00 +1100389 m.marshalValue(out, prop, sliceVal, indent+m.Indent)
Buck Clay67cbcad2015-07-01 11:17:11 +1000390 comma = ","
391 }
392 if m.Indent != "" {
393 out.write("\n")
394 out.write(indent)
395 out.write(m.Indent)
396 }
397 out.write("]")
398 return out.err
399 }
400
David Symonds552c7b92016-02-26 12:45:00 +1100401 // Handle well-known types.
402 // Most are handled up in marshalObject (because 99% are messages).
403 type wkt interface {
404 XXX_WellKnownType() string
405 }
406 if wkt, ok := v.Interface().(wkt); ok {
407 switch wkt.XXX_WellKnownType() {
408 case "NullValue":
409 out.write("null")
410 return out.err
411 }
412 }
413
Buck Clay67cbcad2015-07-01 11:17:11 +1000414 // Handle enumerations.
Juraj Stachof9dd6932015-11-03 18:47:00 +1100415 if !m.EnumsAsInts && prop.Enum != "" {
Buck Clay67cbcad2015-07-01 11:17:11 +1000416 // Unknown enum values will are stringified by the proto library as their
David Symonds0ea3c032015-10-19 13:53:00 +1100417 // value. Such values should _not_ be quoted or they will be interpreted
Buck Clay67cbcad2015-07-01 11:17:11 +1000418 // as an enum string instead of their value.
419 enumStr := v.Interface().(fmt.Stringer).String()
420 var valStr string
421 if v.Kind() == reflect.Ptr {
422 valStr = strconv.Itoa(int(v.Elem().Int()))
423 } else {
424 valStr = strconv.Itoa(int(v.Int()))
425 }
426 isKnownEnum := enumStr != valStr
427 if isKnownEnum {
428 out.write(`"`)
429 }
430 out.write(enumStr)
431 if isKnownEnum {
432 out.write(`"`)
433 }
434 return out.err
435 }
436
437 // Handle nested messages.
438 if v.Kind() == reflect.Struct {
David Symondsbf531ff2016-04-20 14:40:00 +1000439 return m.marshalObject(out, v.Addr().Interface().(proto.Message), indent+m.Indent, "")
Buck Clay67cbcad2015-07-01 11:17:11 +1000440 }
441
442 // Handle maps.
David Symonds3fe63ce2015-08-07 15:00:00 +1000443 // Since Go randomizes map iteration, we sort keys for stable output.
Buck Clay67cbcad2015-07-01 11:17:11 +1000444 if v.Kind() == reflect.Map {
445 out.write(`{`)
446 keys := v.MapKeys()
447 sort.Sort(mapKeys(keys))
448 for i, k := range keys {
449 if i > 0 {
450 out.write(`,`)
451 }
452 if m.Indent != "" {
453 out.write("\n")
454 out.write(indent)
455 out.write(m.Indent)
456 out.write(m.Indent)
457 }
458
459 b, err := json.Marshal(k.Interface())
460 if err != nil {
461 return err
462 }
463 s := string(b)
464
465 // If the JSON is not a string value, encode it again to make it one.
466 if !strings.HasPrefix(s, `"`) {
467 b, err := json.Marshal(s)
468 if err != nil {
469 return err
470 }
471 s = string(b)
472 }
473
474 out.write(s)
475 out.write(`:`)
476 if m.Indent != "" {
477 out.write(` `)
478 }
479
Juraj Stachof9dd6932015-11-03 18:47:00 +1100480 if err := m.marshalValue(out, prop, v.MapIndex(k), indent+m.Indent); err != nil {
Buck Clay67cbcad2015-07-01 11:17:11 +1000481 return err
482 }
483 }
484 if m.Indent != "" {
485 out.write("\n")
486 out.write(indent)
487 out.write(m.Indent)
488 }
489 out.write(`}`)
490 return out.err
491 }
492
493 // Default handling defers to the encoding/json library.
494 b, err := json.Marshal(v.Interface())
495 if err != nil {
496 return err
497 }
498 needToQuote := string(b[0]) != `"` && (v.Kind() == reflect.Int64 || v.Kind() == reflect.Uint64)
499 if needToQuote {
500 out.write(`"`)
501 }
502 out.write(string(b))
503 if needToQuote {
504 out.write(`"`)
505 }
506 return out.err
507}
508
Nikki VonHollen99511272016-03-10 21:13:00 +1100509// UnmarshalNext unmarshals the next protocol buffer from a JSON object stream.
510// This function is lenient and will decode any options permutations of the
511// related Marshaler.
512func UnmarshalNext(dec *json.Decoder, pb proto.Message) error {
513 inputValue := json.RawMessage{}
514 if err := dec.Decode(&inputValue); err != nil {
515 return err
516 }
David Symondsf0a097d2016-04-13 14:01:00 +1000517 return unmarshalValue(reflect.ValueOf(pb).Elem(), inputValue, nil)
Nikki VonHollen99511272016-03-10 21:13:00 +1100518}
519
Buck Clay67cbcad2015-07-01 11:17:11 +1000520// Unmarshal unmarshals a JSON object stream into a protocol
521// buffer. This function is lenient and will decode any options
David Symonds21f81362015-08-17 11:59:00 +1000522// permutations of the related Marshaler.
Buck Clay67cbcad2015-07-01 11:17:11 +1000523func Unmarshal(r io.Reader, pb proto.Message) error {
Nikki VonHollen99511272016-03-10 21:13:00 +1100524 dec := json.NewDecoder(r)
525 return UnmarshalNext(dec, pb)
Buck Clay67cbcad2015-07-01 11:17:11 +1000526}
527
528// UnmarshalString will populate the fields of a protocol buffer based
529// on a JSON string. This function is lenient and will decode any options
David Symonds21f81362015-08-17 11:59:00 +1000530// permutations of the related Marshaler.
Buck Clay67cbcad2015-07-01 11:17:11 +1000531func UnmarshalString(str string, pb proto.Message) error {
David Symonds5d7f79b2015-10-19 11:37:00 +1100532 return Unmarshal(strings.NewReader(str), pb)
Buck Clay67cbcad2015-07-01 11:17:11 +1000533}
534
535// unmarshalValue converts/copies a value into the target.
David Symondsf0a097d2016-04-13 14:01:00 +1000536// prop may be nil.
537func unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *proto.Properties) error {
Buck Clay67cbcad2015-07-01 11:17:11 +1000538 targetType := target.Type()
539
540 // Allocate memory for pointer fields.
541 if targetType.Kind() == reflect.Ptr {
542 target.Set(reflect.New(targetType.Elem()))
David Symondsf0a097d2016-04-13 14:01:00 +1000543 return unmarshalValue(target.Elem(), inputValue, prop)
Buck Clay67cbcad2015-07-01 11:17:11 +1000544 }
545
David Symonds553c7642016-02-23 14:14:00 +1100546 // Handle well-known types.
547 type wkt interface {
548 XXX_WellKnownType() string
549 }
550 if wkt, ok := target.Addr().Interface().(wkt); ok {
551 switch wkt.XXX_WellKnownType() {
David Symondsc6184d32016-02-25 11:12:00 +1100552 case "DoubleValue", "FloatValue", "Int64Value", "UInt64Value",
553 "Int32Value", "UInt32Value", "BoolValue", "StringValue", "BytesValue":
554 // "Wrappers use the same representation in JSON
555 // as the wrapped primitive type, except that null is allowed."
556 // encoding/json will turn JSON `null` into Go `nil`,
557 // so we don't have to do any extra work.
David Symondsf0a097d2016-04-13 14:01:00 +1000558 return unmarshalValue(target.Field(0), inputValue, prop)
David Symondsbf531ff2016-04-20 14:40:00 +1000559 case "Any":
560 return fmt.Errorf("unmarshaling Any not supported yet")
David Symonds553c7642016-02-23 14:14:00 +1100561 case "Duration":
562 unq, err := strconv.Unquote(string(inputValue))
563 if err != nil {
564 return err
565 }
566 d, err := time.ParseDuration(unq)
567 if err != nil {
568 return fmt.Errorf("bad Duration: %v", err)
569 }
570 ns := d.Nanoseconds()
571 s := ns / 1e9
572 ns %= 1e9
573 target.Field(0).SetInt(s)
574 target.Field(1).SetInt(ns)
575 return nil
576 case "Timestamp":
577 unq, err := strconv.Unquote(string(inputValue))
578 if err != nil {
579 return err
580 }
581 t, err := time.Parse(time.RFC3339Nano, unq)
582 if err != nil {
583 return fmt.Errorf("bad Timestamp: %v", err)
584 }
585 ns := t.UnixNano()
586 s := ns / 1e9
587 ns %= 1e9
588 target.Field(0).SetInt(s)
589 target.Field(1).SetInt(ns)
590 return nil
591 }
592 }
593
David Symondsf0a097d2016-04-13 14:01:00 +1000594 // Handle enums, which have an underlying type of int32,
595 // and may appear as strings.
596 // The case of an enum appearing as a number is handled
597 // at the bottom of this function.
598 if inputValue[0] == '"' && prop != nil && prop.Enum != "" {
599 vmap := proto.EnumValueMap(prop.Enum)
600 // Don't need to do unquoting; valid enum names
601 // are from a limited character set.
602 s := inputValue[1 : len(inputValue)-1]
603 n, ok := vmap[string(s)]
604 if !ok {
605 return fmt.Errorf("unknown value %q for enum %s", s, prop.Enum)
606 }
607 if target.Kind() == reflect.Ptr { // proto2
608 target.Set(reflect.New(targetType.Elem()))
609 target = target.Elem()
610 }
611 target.SetInt(int64(n))
612 return nil
613 }
614
Buck Clay67cbcad2015-07-01 11:17:11 +1000615 // Handle nested messages.
616 if targetType.Kind() == reflect.Struct {
617 var jsonFields map[string]json.RawMessage
618 if err := json.Unmarshal(inputValue, &jsonFields); err != nil {
619 return err
620 }
621
David Symonds62e43642016-03-17 12:31:00 +1100622 consumeField := func(prop *proto.Properties) (json.RawMessage, bool) {
623 // Be liberal in what names we accept; both orig_name and camelName are okay.
624 fieldNames := acceptedJSONFieldNames(prop)
625
626 vOrig, okOrig := jsonFields[fieldNames.orig]
627 vCamel, okCamel := jsonFields[fieldNames.camel]
628 if !okOrig && !okCamel {
629 return nil, false
630 }
631 // If, for some reason, both are present in the data, favour the camelName.
632 var raw json.RawMessage
633 if okOrig {
634 raw = vOrig
635 delete(jsonFields, fieldNames.orig)
636 }
637 if okCamel {
638 raw = vCamel
639 delete(jsonFields, fieldNames.camel)
640 }
641 return raw, true
642 }
643
David Symonds5d7f79b2015-10-19 11:37:00 +1100644 sprops := proto.GetProperties(targetType)
Buck Clay67cbcad2015-07-01 11:17:11 +1000645 for i := 0; i < target.NumField(); i++ {
David Symonds31db5692015-08-10 12:45:00 +1000646 ft := target.Type().Field(i)
647 if strings.HasPrefix(ft.Name, "XXX_") {
Buck Clay67cbcad2015-07-01 11:17:11 +1000648 continue
649 }
650
David Symonds62e43642016-03-17 12:31:00 +1100651 valueForField, ok := consumeField(sprops.Prop[i])
652 if !ok {
David Symonds5d7f79b2015-10-19 11:37:00 +1100653 continue
654 }
David Symonds5d7f79b2015-10-19 11:37:00 +1100655
David Symondsf0a097d2016-04-13 14:01:00 +1000656 if err := unmarshalValue(target.Field(i), valueForField, sprops.Prop[i]); err != nil {
David Symonds5d7f79b2015-10-19 11:37:00 +1100657 return err
Buck Clay67cbcad2015-07-01 11:17:11 +1000658 }
659 }
David Symonds59b73b32015-08-24 13:22:02 +1000660 // Check for any oneof fields.
David Symonds62e43642016-03-17 12:31:00 +1100661 if len(jsonFields) > 0 {
662 for _, oop := range sprops.OneofTypes {
663 raw, ok := consumeField(oop.Prop)
664 if !ok {
665 continue
666 }
David Symonds1baed092015-08-25 15:42:00 +1000667 nv := reflect.New(oop.Type.Elem())
668 target.Field(oop.Field).Set(nv)
David Symondsf0a097d2016-04-13 14:01:00 +1000669 if err := unmarshalValue(nv.Elem().Field(0), raw, oop.Prop); err != nil {
David Symonds59b73b32015-08-24 13:22:02 +1000670 return err
671 }
David Symonds59b73b32015-08-24 13:22:02 +1000672 }
673 }
David Symonds3fe63ce2015-08-07 15:00:00 +1000674 if len(jsonFields) > 0 {
675 // Pick any field to be the scapegoat.
676 var f string
677 for fname := range jsonFields {
678 f = fname
679 break
680 }
681 return fmt.Errorf("unknown field %q in %v", f, targetType)
682 }
Buck Clay67cbcad2015-07-01 11:17:11 +1000683 return nil
684 }
685
686 // Handle arrays (which aren't encoded bytes)
687 if targetType != byteArrayType && targetType.Kind() == reflect.Slice {
688 var slc []json.RawMessage
689 if err := json.Unmarshal(inputValue, &slc); err != nil {
690 return err
691 }
692 len := len(slc)
693 target.Set(reflect.MakeSlice(targetType, len, len))
694 for i := 0; i < len; i++ {
David Symondsf0a097d2016-04-13 14:01:00 +1000695 if err := unmarshalValue(target.Index(i), slc[i], prop); err != nil {
Buck Clay67cbcad2015-07-01 11:17:11 +1000696 return err
697 }
698 }
699 return nil
700 }
701
702 // Handle maps (whose keys are always strings)
703 if targetType.Kind() == reflect.Map {
704 var mp map[string]json.RawMessage
705 if err := json.Unmarshal(inputValue, &mp); err != nil {
706 return err
707 }
708 target.Set(reflect.MakeMap(targetType))
David Symondsf0a097d2016-04-13 14:01:00 +1000709 var keyprop, valprop *proto.Properties
710 if prop != nil {
711 // These could still be nil if the protobuf metadata is broken somehow.
712 // TODO: This won't work because the fields are unexported.
713 // We should probably just reparse them.
714 //keyprop, valprop = prop.mkeyprop, prop.mvalprop
715 }
Buck Clay67cbcad2015-07-01 11:17:11 +1000716 for ks, raw := range mp {
717 // Unmarshal map key. The core json library already decoded the key into a
718 // string, so we handle that specially. Other types were quoted post-serialization.
719 var k reflect.Value
720 if targetType.Key().Kind() == reflect.String {
721 k = reflect.ValueOf(ks)
722 } else {
723 k = reflect.New(targetType.Key()).Elem()
David Symondsf0a097d2016-04-13 14:01:00 +1000724 if err := unmarshalValue(k, json.RawMessage(ks), keyprop); err != nil {
Buck Clay67cbcad2015-07-01 11:17:11 +1000725 return err
726 }
727 }
728
729 // Unmarshal map value.
730 v := reflect.New(targetType.Elem()).Elem()
David Symondsf0a097d2016-04-13 14:01:00 +1000731 if err := unmarshalValue(v, raw, valprop); err != nil {
Buck Clay67cbcad2015-07-01 11:17:11 +1000732 return err
733 }
734 target.SetMapIndex(k, v)
735 }
736 return nil
737 }
738
739 // 64-bit integers can be encoded as strings. In this case we drop
740 // the quotes and proceed as normal.
741 isNum := targetType.Kind() == reflect.Int64 || targetType.Kind() == reflect.Uint64
742 if isNum && strings.HasPrefix(string(inputValue), `"`) {
743 inputValue = inputValue[1 : len(inputValue)-1]
744 }
745
746 // Use the encoding/json for parsing other value types.
747 return json.Unmarshal(inputValue, target.Addr().Interface())
748}
749
David Symonds001690d2016-02-10 14:04:11 +1100750// jsonProperties returns parsed proto.Properties for the field and corrects JSONName attribute.
751func jsonProperties(f reflect.StructField, origName bool) *proto.Properties {
David Symonds31db5692015-08-10 12:45:00 +1000752 var prop proto.Properties
753 prop.Init(f.Type, f.Name, f.Tag.Get("protobuf"), &f)
David Symonds001690d2016-02-10 14:04:11 +1100754 if origName || prop.JSONName == "" {
755 prop.JSONName = prop.OrigName
756 }
Juraj Stachof9dd6932015-11-03 18:47:00 +1100757 return &prop
758}
759
David Symonds001690d2016-02-10 14:04:11 +1100760type fieldNames struct {
761 orig, camel string
762}
763
David Symonds62e43642016-03-17 12:31:00 +1100764func acceptedJSONFieldNames(prop *proto.Properties) fieldNames {
David Symonds001690d2016-02-10 14:04:11 +1100765 opts := fieldNames{orig: prop.OrigName, camel: prop.OrigName}
766 if prop.JSONName != "" {
767 opts.camel = prop.JSONName
768 }
769 return opts
770}
771
Buck Clay67cbcad2015-07-01 11:17:11 +1000772// Writer wrapper inspired by https://blog.golang.org/errors-are-values
773type errWriter struct {
774 writer io.Writer
775 err error
776}
777
778func (w *errWriter) write(str string) {
779 if w.err != nil {
780 return
781 }
782 _, w.err = w.writer.Write([]byte(str))
783}
784
785// Map fields may have key types of non-float scalars, strings and enums.
786// The easiest way to sort them in some deterministic order is to use fmt.
787// If this turns out to be inefficient we can always consider other options,
788// such as doing a Schwartzian transform.
789type mapKeys []reflect.Value
790
791func (s mapKeys) Len() int { return len(s) }
792func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
793func (s mapKeys) Less(i, j int) bool {
794 return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface())
795}