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