| // 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 |
| } |
| } |