aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/golang/protobuf/jsonpb/encode.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/golang/protobuf/jsonpb/encode.go')
-rw-r--r--vendor/github.com/golang/protobuf/jsonpb/encode.go559
1 files changed, 559 insertions, 0 deletions
diff --git a/vendor/github.com/golang/protobuf/jsonpb/encode.go b/vendor/github.com/golang/protobuf/jsonpb/encode.go
new file mode 100644
index 000000000..685c80a62
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/jsonpb/encode.go
@@ -0,0 +1,559 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package jsonpb
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/golang/protobuf/proto"
+ "google.golang.org/protobuf/encoding/protojson"
+ protoV2 "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/reflect/protoreflect"
+ "google.golang.org/protobuf/reflect/protoregistry"
+)
+
+const wrapJSONMarshalV2 = false
+
+// Marshaler is a configurable object for marshaling protocol buffer messages
+// to the specified JSON representation.
+type Marshaler struct {
+ // OrigName specifies whether to use the original protobuf name for fields.
+ OrigName bool
+
+ // EnumsAsInts specifies whether to render enum values as integers,
+ // as opposed to string values.
+ EnumsAsInts bool
+
+ // EmitDefaults specifies whether to render fields with zero values.
+ EmitDefaults bool
+
+ // Indent controls whether the output is compact or not.
+ // If empty, the output is compact JSON. Otherwise, every JSON object
+ // entry and JSON array value will be on its own line.
+ // Each line will be preceded by repeated copies of Indent, where the
+ // number of copies is the current indentation depth.
+ Indent string
+
+ // AnyResolver is used to resolve the google.protobuf.Any well-known type.
+ // If unset, the global registry is used by default.
+ AnyResolver AnyResolver
+}
+
+// JSONPBMarshaler is implemented by protobuf messages that customize the
+// way they are marshaled to JSON. Messages that implement this should also
+// implement JSONPBUnmarshaler so that the custom format can be parsed.
+//
+// The JSON marshaling must follow the proto to JSON specification:
+// https://developers.google.com/protocol-buffers/docs/proto3#json
+//
+// Deprecated: Custom types should implement protobuf reflection instead.
+type JSONPBMarshaler interface {
+ MarshalJSONPB(*Marshaler) ([]byte, error)
+}
+
+// Marshal serializes a protobuf message as JSON into w.
+func (jm *Marshaler) Marshal(w io.Writer, m proto.Message) error {
+ b, err := jm.marshal(m)
+ if len(b) > 0 {
+ if _, err := w.Write(b); err != nil {
+ return err
+ }
+ }
+ return err
+}
+
+// MarshalToString serializes a protobuf message as JSON in string form.
+func (jm *Marshaler) MarshalToString(m proto.Message) (string, error) {
+ b, err := jm.marshal(m)
+ if err != nil {
+ return "", err
+ }
+ return string(b), nil
+}
+
+func (jm *Marshaler) marshal(m proto.Message) ([]byte, error) {
+ v := reflect.ValueOf(m)
+ if m == nil || (v.Kind() == reflect.Ptr && v.IsNil()) {
+ return nil, errors.New("Marshal called with nil")
+ }
+
+ // Check for custom marshalers first since they may not properly
+ // implement protobuf reflection that the logic below relies on.
+ if jsm, ok := m.(JSONPBMarshaler); ok {
+ return jsm.MarshalJSONPB(jm)
+ }
+
+ if wrapJSONMarshalV2 {
+ opts := protojson.MarshalOptions{
+ UseProtoNames: jm.OrigName,
+ UseEnumNumbers: jm.EnumsAsInts,
+ EmitUnpopulated: jm.EmitDefaults,
+ Indent: jm.Indent,
+ }
+ if jm.AnyResolver != nil {
+ opts.Resolver = anyResolver{jm.AnyResolver}
+ }
+ return opts.Marshal(proto.MessageReflect(m).Interface())
+ } else {
+ // Check for unpopulated required fields first.
+ m2 := proto.MessageReflect(m)
+ if err := protoV2.CheckInitialized(m2.Interface()); err != nil {
+ return nil, err
+ }
+
+ w := jsonWriter{Marshaler: jm}
+ err := w.marshalMessage(m2, "", "")
+ return w.buf, err
+ }
+}
+
+type jsonWriter struct {
+ *Marshaler
+ buf []byte
+}
+
+func (w *jsonWriter) write(s string) {
+ w.buf = append(w.buf, s...)
+}
+
+func (w *jsonWriter) marshalMessage(m protoreflect.Message, indent, typeURL string) error {
+ if jsm, ok := proto.MessageV1(m.Interface()).(JSONPBMarshaler); ok {
+ b, err := jsm.MarshalJSONPB(w.Marshaler)
+ if err != nil {
+ return err
+ }
+ if typeURL != "" {
+ // we are marshaling this object to an Any type
+ var js map[string]*json.RawMessage
+ if err = json.Unmarshal(b, &js); err != nil {
+ return fmt.Errorf("type %T produced invalid JSON: %v", m.Interface(), err)
+ }
+ turl, err := json.Marshal(typeURL)
+ if err != nil {
+ return fmt.Errorf("failed to marshal type URL %q to JSON: %v", typeURL, err)
+ }
+ js["@type"] = (*json.RawMessage)(&turl)
+ if b, err = json.Marshal(js); err != nil {
+ return err
+ }
+ }
+ w.write(string(b))
+ return nil
+ }
+
+ md := m.Descriptor()
+ fds := md.Fields()
+
+ // Handle well-known types.
+ const secondInNanos = int64(time.Second / time.Nanosecond)
+ switch wellKnownType(md.FullName()) {
+ case "Any":
+ return w.marshalAny(m, indent)
+ case "BoolValue", "BytesValue", "StringValue",
+ "Int32Value", "UInt32Value", "FloatValue",
+ "Int64Value", "UInt64Value", "DoubleValue":
+ fd := fds.ByNumber(1)
+ return w.marshalValue(fd, m.Get(fd), indent)
+ case "Duration":
+ const maxSecondsInDuration = 315576000000
+ // "Generated output always contains 0, 3, 6, or 9 fractional digits,
+ // depending on required precision."
+ s := m.Get(fds.ByNumber(1)).Int()
+ ns := m.Get(fds.ByNumber(2)).Int()
+ if s < -maxSecondsInDuration || s > maxSecondsInDuration {
+ return fmt.Errorf("seconds out of range %v", s)
+ }
+ if ns <= -secondInNanos || ns >= secondInNanos {
+ return fmt.Errorf("ns out of range (%v, %v)", -secondInNanos, secondInNanos)
+ }
+ if (s > 0 && ns < 0) || (s < 0 && ns > 0) {
+ return errors.New("signs of seconds and nanos do not match")
+ }
+ var sign string
+ if s < 0 || ns < 0 {
+ sign, s, ns = "-", -1*s, -1*ns
+ }
+ x := fmt.Sprintf("%s%d.%09d", sign, s, ns)
+ x = strings.TrimSuffix(x, "000")
+ x = strings.TrimSuffix(x, "000")
+ x = strings.TrimSuffix(x, ".000")
+ w.write(fmt.Sprintf(`"%vs"`, x))
+ return nil
+ case "Timestamp":
+ // "RFC 3339, where generated output will always be Z-normalized
+ // and uses 0, 3, 6 or 9 fractional digits."
+ s := m.Get(fds.ByNumber(1)).Int()
+ ns := m.Get(fds.ByNumber(2)).Int()
+ if ns < 0 || ns >= secondInNanos {
+ return fmt.Errorf("ns out of range [0, %v)", secondInNanos)
+ }
+ t := time.Unix(s, ns).UTC()
+ // time.RFC3339Nano isn't exactly right (we need to get 3/6/9 fractional digits).
+ x := t.Format("2006-01-02T15:04:05.000000000")
+ x = strings.TrimSuffix(x, "000")
+ x = strings.TrimSuffix(x, "000")
+ x = strings.TrimSuffix(x, ".000")
+ w.write(fmt.Sprintf(`"%vZ"`, x))
+ return nil
+ case "Value":
+ // JSON value; which is a null, number, string, bool, object, or array.
+ od := md.Oneofs().Get(0)
+ fd := m.WhichOneof(od)
+ if fd == nil {
+ return errors.New("nil Value")
+ }
+ return w.marshalValue(fd, m.Get(fd), indent)
+ case "Struct", "ListValue":
+ // JSON object or array.
+ fd := fds.ByNumber(1)
+ return w.marshalValue(fd, m.Get(fd), indent)
+ }
+
+ w.write("{")
+ if w.Indent != "" {
+ w.write("\n")
+ }
+
+ firstField := true
+ if typeURL != "" {
+ if err := w.marshalTypeURL(indent, typeURL); err != nil {
+ return err
+ }
+ firstField = false
+ }
+
+ for i := 0; i < fds.Len(); {
+ fd := fds.Get(i)
+ if od := fd.ContainingOneof(); od != nil {
+ fd = m.WhichOneof(od)
+ i += od.Fields().Len()
+ if fd == nil {
+ continue
+ }
+ } else {
+ i++
+ }
+
+ v := m.Get(fd)
+
+ if !m.Has(fd) {
+ if !w.EmitDefaults || fd.ContainingOneof() != nil {
+ continue
+ }
+ if fd.Cardinality() != protoreflect.Repeated && (fd.Message() != nil || fd.Syntax() == protoreflect.Proto2) {
+ v = protoreflect.Value{} // use "null" for singular messages or proto2 scalars
+ }
+ }
+
+ if !firstField {
+ w.writeComma()
+ }
+ if err := w.marshalField(fd, v, indent); err != nil {
+ return err
+ }
+ firstField = false
+ }
+
+ // Handle proto2 extensions.
+ if md.ExtensionRanges().Len() > 0 {
+ // Collect a sorted list of all extension descriptor and values.
+ type ext struct {
+ desc protoreflect.FieldDescriptor
+ val protoreflect.Value
+ }
+ var exts []ext
+ m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
+ if fd.IsExtension() {
+ exts = append(exts, ext{fd, v})
+ }
+ return true
+ })
+ sort.Slice(exts, func(i, j int) bool {
+ return exts[i].desc.Number() < exts[j].desc.Number()
+ })
+
+ for _, ext := range exts {
+ if !firstField {
+ w.writeComma()
+ }
+ if err := w.marshalField(ext.desc, ext.val, indent); err != nil {
+ return err
+ }
+ firstField = false
+ }
+ }
+
+ if w.Indent != "" {
+ w.write("\n")
+ w.write(indent)
+ }
+ w.write("}")
+ return nil
+}
+
+func (w *jsonWriter) writeComma() {
+ if w.Indent != "" {
+ w.write(",\n")
+ } else {
+ w.write(",")
+ }
+}
+
+func (w *jsonWriter) marshalAny(m protoreflect.Message, indent string) error {
+ // "If the Any contains a value that has a special JSON mapping,
+ // it will be converted as follows: {"@type": xxx, "value": yyy}.
+ // Otherwise, the value will be converted into a JSON object,
+ // and the "@type" field will be inserted to indicate the actual data type."
+ md := m.Descriptor()
+ typeURL := m.Get(md.Fields().ByNumber(1)).String()
+ rawVal := m.Get(md.Fields().ByNumber(2)).Bytes()
+
+ var m2 protoreflect.Message
+ if w.AnyResolver != nil {
+ mi, err := w.AnyResolver.Resolve(typeURL)
+ if err != nil {
+ return err
+ }
+ m2 = proto.MessageReflect(mi)
+ } else {
+ mt, err := protoregistry.GlobalTypes.FindMessageByURL(typeURL)
+ if err != nil {
+ return err
+ }
+ m2 = mt.New()
+ }
+
+ if err := protoV2.Unmarshal(rawVal, m2.Interface()); err != nil {
+ return err
+ }
+
+ if wellKnownType(m2.Descriptor().FullName()) == "" {
+ return w.marshalMessage(m2, indent, typeURL)
+ }
+
+ w.write("{")
+ if w.Indent != "" {
+ w.write("\n")
+ }
+ if err := w.marshalTypeURL(indent, typeURL); err != nil {
+ return err
+ }
+ w.writeComma()
+ if w.Indent != "" {
+ w.write(indent)
+ w.write(w.Indent)
+ w.write(`"value": `)
+ } else {
+ w.write(`"value":`)
+ }
+ if err := w.marshalMessage(m2, indent+w.Indent, ""); err != nil {
+ return err
+ }
+ if w.Indent != "" {
+ w.write("\n")
+ w.write(indent)
+ }
+ w.write("}")
+ return nil
+}
+
+func (w *jsonWriter) marshalTypeURL(indent, typeURL string) error {
+ if w.Indent != "" {
+ w.write(indent)
+ w.write(w.Indent)
+ }
+ w.write(`"@type":`)
+ if w.Indent != "" {
+ w.write(" ")
+ }
+ b, err := json.Marshal(typeURL)
+ if err != nil {
+ return err
+ }
+ w.write(string(b))
+ return nil
+}
+
+// marshalField writes field description and value to the Writer.
+func (w *jsonWriter) marshalField(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
+ if w.Indent != "" {
+ w.write(indent)
+ w.write(w.Indent)
+ }
+ w.write(`"`)
+ switch {
+ case fd.IsExtension():
+ // For message set, use the fname of the message as the extension name.
+ name := string(fd.FullName())
+ if isMessageSet(fd.ContainingMessage()) {
+ name = strings.TrimSuffix(name, ".message_set_extension")
+ }
+
+ w.write("[" + name + "]")
+ case w.OrigName:
+ name := string(fd.Name())
+ if fd.Kind() == protoreflect.GroupKind {
+ name = string(fd.Message().Name())
+ }
+ w.write(name)
+ default:
+ w.write(string(fd.JSONName()))
+ }
+ w.write(`":`)
+ if w.Indent != "" {
+ w.write(" ")
+ }
+ return w.marshalValue(fd, v, indent)
+}
+
+func (w *jsonWriter) marshalValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
+ switch {
+ case fd.IsList():
+ w.write("[")
+ comma := ""
+ lv := v.List()
+ for i := 0; i < lv.Len(); i++ {
+ w.write(comma)
+ if w.Indent != "" {
+ w.write("\n")
+ w.write(indent)
+ w.write(w.Indent)
+ w.write(w.Indent)
+ }
+ if err := w.marshalSingularValue(fd, lv.Get(i), indent+w.Indent); err != nil {
+ return err
+ }
+ comma = ","
+ }
+ if w.Indent != "" {
+ w.write("\n")
+ w.write(indent)
+ w.write(w.Indent)
+ }
+ w.write("]")
+ return nil
+ case fd.IsMap():
+ kfd := fd.MapKey()
+ vfd := fd.MapValue()
+ mv := v.Map()
+
+ // Collect a sorted list of all map keys and values.
+ type entry struct{ key, val protoreflect.Value }
+ var entries []entry
+ mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
+ entries = append(entries, entry{k.Value(), v})
+ return true
+ })
+ sort.Slice(entries, func(i, j int) bool {
+ switch kfd.Kind() {
+ case protoreflect.BoolKind:
+ return !entries[i].key.Bool() && entries[j].key.Bool()
+ case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
+ return entries[i].key.Int() < entries[j].key.Int()
+ case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
+ return entries[i].key.Uint() < entries[j].key.Uint()
+ case protoreflect.StringKind:
+ return entries[i].key.String() < entries[j].key.String()
+ default:
+ panic("invalid kind")
+ }
+ })
+
+ w.write(`{`)
+ comma := ""
+ for _, entry := range entries {
+ w.write(comma)
+ if w.Indent != "" {
+ w.write("\n")
+ w.write(indent)
+ w.write(w.Indent)
+ w.write(w.Indent)
+ }
+
+ s := fmt.Sprint(entry.key.Interface())
+ b, err := json.Marshal(s)
+ if err != nil {
+ return err
+ }
+ w.write(string(b))
+
+ w.write(`:`)
+ if w.Indent != "" {
+ w.write(` `)
+ }
+
+ if err := w.marshalSingularValue(vfd, entry.val, indent+w.Indent); err != nil {
+ return err
+ }
+ comma = ","
+ }
+ if w.Indent != "" {
+ w.write("\n")
+ w.write(indent)
+ w.write(w.Indent)
+ }
+ w.write(`}`)
+ return nil
+ default:
+ return w.marshalSingularValue(fd, v, indent)
+ }
+}
+
+func (w *jsonWriter) marshalSingularValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
+ switch {
+ case !v.IsValid():
+ w.write("null")
+ return nil
+ case fd.Message() != nil:
+ return w.marshalMessage(v.Message(), indent+w.Indent, "")
+ case fd.Enum() != nil:
+ if fd.Enum().FullName() == "google.protobuf.NullValue" {
+ w.write("null")
+ return nil
+ }
+
+ vd := fd.Enum().Values().ByNumber(v.Enum())
+ if vd == nil || w.EnumsAsInts {
+ w.write(strconv.Itoa(int(v.Enum())))
+ } else {
+ w.write(`"` + string(vd.Name()) + `"`)
+ }
+ return nil
+ default:
+ switch v.Interface().(type) {
+ case float32, float64:
+ switch {
+ case math.IsInf(v.Float(), +1):
+ w.write(`"Infinity"`)
+ return nil
+ case math.IsInf(v.Float(), -1):
+ w.write(`"-Infinity"`)
+ return nil
+ case math.IsNaN(v.Float()):
+ w.write(`"NaN"`)
+ return nil
+ }
+ case int64, uint64:
+ w.write(fmt.Sprintf(`"%d"`, v.Interface()))
+ return nil
+ }
+
+ b, err := json.Marshal(v.Interface())
+ if err != nil {
+ return err
+ }
+ w.write(string(b))
+ return nil
+ }
+}