| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 1 | // 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 | /* |
| 33 | Package jsonpb provides marshalling/unmarshalling functionality between |
| 34 | protocol buffer and JSON objects. |
| 35 | |
| 36 | Compared to encoding/json, this library: |
| 37 | - encodes int64, uint64 as strings |
| 38 | - optionally encodes enums as strings |
| 39 | */ |
| 40 | package jsonpb |
| 41 | |
| 42 | import ( |
| 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 | |
| 55 | var ( |
| 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 |
| 61 | type 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. |
| 73 | func (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. |
| 79 | func (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. |
| 88 | func (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) |
| 99 | fieldName, omitFieldIfNil := parseFieldOptions(valueField) |
| 100 | |
| 101 | // Fields which should not be serialized will specify a json tag with '-' |
| 102 | // TODO: proto3 objects should have default values omitted. |
| 103 | if fieldName == "-" { |
| 104 | continue |
| 105 | } else if omitFieldIfNil { |
| 106 | // IsNil will panic on most value kinds. |
| 107 | skip := false |
| 108 | switch value.Kind() { |
| 109 | case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: |
| 110 | skip = value.IsNil() |
| 111 | } |
| 112 | if skip { |
| 113 | continue |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | out.write(writeBeforeField) |
| 118 | if m.Indent != "" { |
| 119 | out.write(indent) |
| 120 | out.write(m.Indent) |
| 121 | } |
| 122 | out.write(`"`) |
| 123 | out.write(fieldName) |
| 124 | out.write(`":`) |
| 125 | if m.Indent != "" { |
| 126 | out.write(" ") |
| 127 | } |
| 128 | |
| 129 | if err := m.marshalValue(out, value, valueField, indent); err != nil { |
| 130 | return err |
| 131 | } |
| 132 | |
| 133 | if m.Indent != "" { |
| 134 | writeBeforeField = ",\n" |
| 135 | } else { |
| 136 | writeBeforeField = "," |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | if m.Indent != "" { |
| 141 | out.write("\n") |
| 142 | out.write(indent) |
| 143 | } |
| 144 | out.write("}") |
| 145 | return out.err |
| 146 | } |
| 147 | |
| 148 | // marshalValue writes the value to the Writer. |
| 149 | func (m *Marshaller) marshalValue(out *errWriter, v reflect.Value, |
| 150 | structField reflect.StructField, indent string) error { |
| 151 | |
| 152 | var err error |
| 153 | v = reflect.Indirect(v) |
| 154 | |
| 155 | // Handle repeated elements. |
| 156 | if v.Type() != byteArrayType && v.Kind() == reflect.Slice { |
| 157 | out.write("[") |
| 158 | comma := "" |
| 159 | for i := 0; i < v.Len(); i++ { |
| 160 | sliceVal := v.Index(i) |
| 161 | out.write(comma) |
| 162 | if m.Indent != "" { |
| 163 | out.write("\n") |
| 164 | out.write(indent) |
| 165 | out.write(m.Indent) |
| 166 | out.write(m.Indent) |
| 167 | } |
| 168 | m.marshalValue(out, sliceVal, structField, indent+m.Indent) |
| 169 | comma = "," |
| 170 | } |
| 171 | if m.Indent != "" { |
| 172 | out.write("\n") |
| 173 | out.write(indent) |
| 174 | out.write(m.Indent) |
| 175 | } |
| 176 | out.write("]") |
| 177 | return out.err |
| 178 | } |
| 179 | |
| 180 | // Handle enumerations. |
| 181 | protoInfo := structField.Tag.Get("protobuf") |
| 182 | if m.EnumsAsString && strings.Contains(protoInfo, ",enum=") { |
| 183 | // Unknown enum values will are stringified by the proto library as their |
| 184 | // value. Such values should _not_ be quoted or they will be intrepreted |
| 185 | // as an enum string instead of their value. |
| 186 | enumStr := v.Interface().(fmt.Stringer).String() |
| 187 | var valStr string |
| 188 | if v.Kind() == reflect.Ptr { |
| 189 | valStr = strconv.Itoa(int(v.Elem().Int())) |
| 190 | } else { |
| 191 | valStr = strconv.Itoa(int(v.Int())) |
| 192 | } |
| 193 | isKnownEnum := enumStr != valStr |
| 194 | if isKnownEnum { |
| 195 | out.write(`"`) |
| 196 | } |
| 197 | out.write(enumStr) |
| 198 | if isKnownEnum { |
| 199 | out.write(`"`) |
| 200 | } |
| 201 | return out.err |
| 202 | } |
| 203 | |
| 204 | // Handle nested messages. |
| 205 | if v.Kind() == reflect.Struct { |
| 206 | return m.marshalObject(out, v.Addr().Interface().(proto.Message), indent+m.Indent) |
| 207 | } |
| 208 | |
| 209 | // Handle maps. |
| David Symonds | 3fe63ce | 2015-08-07 15:00:00 +1000 | [diff] [blame^] | 210 | // Since Go randomizes map iteration, we sort keys for stable output. |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 211 | if v.Kind() == reflect.Map { |
| 212 | out.write(`{`) |
| 213 | keys := v.MapKeys() |
| 214 | sort.Sort(mapKeys(keys)) |
| 215 | for i, k := range keys { |
| 216 | if i > 0 { |
| 217 | out.write(`,`) |
| 218 | } |
| 219 | if m.Indent != "" { |
| 220 | out.write("\n") |
| 221 | out.write(indent) |
| 222 | out.write(m.Indent) |
| 223 | out.write(m.Indent) |
| 224 | } |
| 225 | |
| 226 | b, err := json.Marshal(k.Interface()) |
| 227 | if err != nil { |
| 228 | return err |
| 229 | } |
| 230 | s := string(b) |
| 231 | |
| 232 | // If the JSON is not a string value, encode it again to make it one. |
| 233 | if !strings.HasPrefix(s, `"`) { |
| 234 | b, err := json.Marshal(s) |
| 235 | if err != nil { |
| 236 | return err |
| 237 | } |
| 238 | s = string(b) |
| 239 | } |
| 240 | |
| 241 | out.write(s) |
| 242 | out.write(`:`) |
| 243 | if m.Indent != "" { |
| 244 | out.write(` `) |
| 245 | } |
| 246 | |
| 247 | if err := m.marshalValue(out, v.MapIndex(k), structField, indent+m.Indent); err != nil { |
| 248 | return err |
| 249 | } |
| 250 | } |
| 251 | if m.Indent != "" { |
| 252 | out.write("\n") |
| 253 | out.write(indent) |
| 254 | out.write(m.Indent) |
| 255 | } |
| 256 | out.write(`}`) |
| 257 | return out.err |
| 258 | } |
| 259 | |
| 260 | // Default handling defers to the encoding/json library. |
| 261 | b, err := json.Marshal(v.Interface()) |
| 262 | if err != nil { |
| 263 | return err |
| 264 | } |
| 265 | needToQuote := string(b[0]) != `"` && (v.Kind() == reflect.Int64 || v.Kind() == reflect.Uint64) |
| 266 | if needToQuote { |
| 267 | out.write(`"`) |
| 268 | } |
| 269 | out.write(string(b)) |
| 270 | if needToQuote { |
| 271 | out.write(`"`) |
| 272 | } |
| 273 | return out.err |
| 274 | } |
| 275 | |
| 276 | // Unmarshal unmarshals a JSON object stream into a protocol |
| 277 | // buffer. This function is lenient and will decode any options |
| 278 | // permutations of the related Marshaller. |
| 279 | func Unmarshal(r io.Reader, pb proto.Message) error { |
| 280 | inputValue := json.RawMessage{} |
| 281 | if err := json.NewDecoder(r).Decode(&inputValue); err != nil { |
| 282 | return err |
| 283 | } |
| 284 | return unmarshalValue(reflect.ValueOf(pb).Elem(), inputValue) |
| 285 | } |
| 286 | |
| 287 | // UnmarshalString will populate the fields of a protocol buffer based |
| 288 | // on a JSON string. This function is lenient and will decode any options |
| 289 | // permutations of the related Marshaller. |
| 290 | func UnmarshalString(str string, pb proto.Message) error { |
| 291 | return Unmarshal(bytes.NewReader([]byte(str)), pb) |
| 292 | } |
| 293 | |
| 294 | // unmarshalValue converts/copies a value into the target. |
| 295 | func unmarshalValue(target reflect.Value, inputValue json.RawMessage) error { |
| 296 | targetType := target.Type() |
| 297 | |
| 298 | // Allocate memory for pointer fields. |
| 299 | if targetType.Kind() == reflect.Ptr { |
| 300 | target.Set(reflect.New(targetType.Elem())) |
| 301 | return unmarshalValue(target.Elem(), inputValue) |
| 302 | } |
| 303 | |
| 304 | // Handle nested messages. |
| 305 | if targetType.Kind() == reflect.Struct { |
| 306 | var jsonFields map[string]json.RawMessage |
| 307 | if err := json.Unmarshal(inputValue, &jsonFields); err != nil { |
| 308 | return err |
| 309 | } |
| 310 | |
| 311 | for i := 0; i < target.NumField(); i++ { |
| 312 | fieldName, _ := parseFieldOptions(target.Type().Field(i)) |
| 313 | |
| 314 | // Fields which should not be serialized will specify a json tag with '-' |
| 315 | if fieldName == "-" { |
| 316 | continue |
| 317 | } |
| 318 | |
| 319 | if valueForField, ok := jsonFields[fieldName]; ok { |
| 320 | if err := unmarshalValue(target.Field(i), valueForField); err != nil { |
| 321 | return err |
| 322 | } |
| David Symonds | 3fe63ce | 2015-08-07 15:00:00 +1000 | [diff] [blame^] | 323 | delete(jsonFields, fieldName) |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 324 | } |
| 325 | } |
| David Symonds | 3fe63ce | 2015-08-07 15:00:00 +1000 | [diff] [blame^] | 326 | if len(jsonFields) > 0 { |
| 327 | // Pick any field to be the scapegoat. |
| 328 | var f string |
| 329 | for fname := range jsonFields { |
| 330 | f = fname |
| 331 | break |
| 332 | } |
| 333 | return fmt.Errorf("unknown field %q in %v", f, targetType) |
| 334 | } |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 335 | return nil |
| 336 | } |
| 337 | |
| 338 | // Handle arrays (which aren't encoded bytes) |
| 339 | if targetType != byteArrayType && targetType.Kind() == reflect.Slice { |
| 340 | var slc []json.RawMessage |
| 341 | if err := json.Unmarshal(inputValue, &slc); err != nil { |
| 342 | return err |
| 343 | } |
| 344 | len := len(slc) |
| 345 | target.Set(reflect.MakeSlice(targetType, len, len)) |
| 346 | for i := 0; i < len; i++ { |
| 347 | if err := unmarshalValue(target.Index(i), slc[i]); err != nil { |
| 348 | return err |
| 349 | } |
| 350 | } |
| 351 | return nil |
| 352 | } |
| 353 | |
| 354 | // Handle maps (whose keys are always strings) |
| 355 | if targetType.Kind() == reflect.Map { |
| 356 | var mp map[string]json.RawMessage |
| 357 | if err := json.Unmarshal(inputValue, &mp); err != nil { |
| 358 | return err |
| 359 | } |
| 360 | target.Set(reflect.MakeMap(targetType)) |
| 361 | for ks, raw := range mp { |
| 362 | // Unmarshal map key. The core json library already decoded the key into a |
| 363 | // string, so we handle that specially. Other types were quoted post-serialization. |
| 364 | var k reflect.Value |
| 365 | if targetType.Key().Kind() == reflect.String { |
| 366 | k = reflect.ValueOf(ks) |
| 367 | } else { |
| 368 | k = reflect.New(targetType.Key()).Elem() |
| 369 | if err := unmarshalValue(k, json.RawMessage(ks)); err != nil { |
| 370 | return err |
| 371 | } |
| 372 | } |
| 373 | |
| 374 | // Unmarshal map value. |
| 375 | v := reflect.New(targetType.Elem()).Elem() |
| 376 | if err := unmarshalValue(v, raw); err != nil { |
| 377 | return err |
| 378 | } |
| 379 | target.SetMapIndex(k, v) |
| 380 | } |
| 381 | return nil |
| 382 | } |
| 383 | |
| 384 | // 64-bit integers can be encoded as strings. In this case we drop |
| 385 | // the quotes and proceed as normal. |
| 386 | isNum := targetType.Kind() == reflect.Int64 || targetType.Kind() == reflect.Uint64 |
| 387 | if isNum && strings.HasPrefix(string(inputValue), `"`) { |
| 388 | inputValue = inputValue[1 : len(inputValue)-1] |
| 389 | } |
| 390 | |
| 391 | // Use the encoding/json for parsing other value types. |
| 392 | return json.Unmarshal(inputValue, target.Addr().Interface()) |
| 393 | } |
| 394 | |
| 395 | // hasUnmarshalJSON is a interface implemented by protocol buffer enums. |
| 396 | type hasUnmarshalJSON interface { |
| 397 | UnmarshalJSON(data []byte) error |
| 398 | } |
| 399 | |
| 400 | // parseFieldOptions returns the field name and if it should be omited. |
| 401 | func parseFieldOptions(f reflect.StructField) (string, bool) { |
| David Symonds | 3fe63ce | 2015-08-07 15:00:00 +1000 | [diff] [blame^] | 402 | // TODO: Do this without using the "json" field tag. |
| Buck Clay | 67cbcad | 2015-07-01 11:17:11 +1000 | [diff] [blame] | 403 | name := f.Name |
| 404 | omitEmpty := false |
| 405 | tag := f.Tag.Get("json") |
| 406 | tagParts := strings.Split(tag, ",") |
| 407 | for i := range tagParts { |
| 408 | if tagParts[i] == "omitempty" || tagParts[i] == "" { |
| 409 | omitEmpty = true |
| 410 | } else { |
| 411 | name = tagParts[i] |
| 412 | } |
| 413 | } |
| 414 | return name, omitEmpty |
| 415 | } |
| 416 | |
| 417 | // Writer wrapper inspired by https://blog.golang.org/errors-are-values |
| 418 | type errWriter struct { |
| 419 | writer io.Writer |
| 420 | err error |
| 421 | } |
| 422 | |
| 423 | func (w *errWriter) write(str string) { |
| 424 | if w.err != nil { |
| 425 | return |
| 426 | } |
| 427 | _, w.err = w.writer.Write([]byte(str)) |
| 428 | } |
| 429 | |
| 430 | // Map fields may have key types of non-float scalars, strings and enums. |
| 431 | // The easiest way to sort them in some deterministic order is to use fmt. |
| 432 | // If this turns out to be inefficient we can always consider other options, |
| 433 | // such as doing a Schwartzian transform. |
| 434 | type mapKeys []reflect.Value |
| 435 | |
| 436 | func (s mapKeys) Len() int { return len(s) } |
| 437 | func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
| 438 | func (s mapKeys) Less(i, j int) bool { |
| 439 | return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface()) |
| 440 | } |