blob: ba971f07810c62ec82fe02fc615b56de00bc18f3 [file] [log] [blame]
David K. Bainbridgebd6b2882021-08-26 13:31:02 +00001// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package protojson
6
7import (
8 "encoding/base64"
9 "fmt"
10
11 "google.golang.org/protobuf/internal/encoding/json"
12 "google.golang.org/protobuf/internal/encoding/messageset"
13 "google.golang.org/protobuf/internal/errors"
14 "google.golang.org/protobuf/internal/filedesc"
15 "google.golang.org/protobuf/internal/flags"
16 "google.golang.org/protobuf/internal/genid"
17 "google.golang.org/protobuf/internal/order"
18 "google.golang.org/protobuf/internal/pragma"
19 "google.golang.org/protobuf/proto"
20 "google.golang.org/protobuf/reflect/protoreflect"
21 pref "google.golang.org/protobuf/reflect/protoreflect"
22 "google.golang.org/protobuf/reflect/protoregistry"
23)
24
25const defaultIndent = " "
26
27// Format formats the message as a multiline string.
28// This function is only intended for human consumption and ignores errors.
29// Do not depend on the output being stable. It may change over time across
30// different versions of the program.
31func Format(m proto.Message) string {
32 return MarshalOptions{Multiline: true}.Format(m)
33}
34
35// Marshal writes the given proto.Message in JSON format using default options.
36// Do not depend on the output being stable. It may change over time across
37// different versions of the program.
38func Marshal(m proto.Message) ([]byte, error) {
39 return MarshalOptions{}.Marshal(m)
40}
41
42// MarshalOptions is a configurable JSON format marshaler.
43type MarshalOptions struct {
44 pragma.NoUnkeyedLiterals
45
46 // Multiline specifies whether the marshaler should format the output in
47 // indented-form with every textual element on a new line.
48 // If Indent is an empty string, then an arbitrary indent is chosen.
49 Multiline bool
50
51 // Indent specifies the set of indentation characters to use in a multiline
52 // formatted output such that every entry is preceded by Indent and
53 // terminated by a newline. If non-empty, then Multiline is treated as true.
54 // Indent can only be composed of space or tab characters.
55 Indent string
56
57 // AllowPartial allows messages that have missing required fields to marshal
58 // without returning an error. If AllowPartial is false (the default),
59 // Marshal will return error if there are any missing required fields.
60 AllowPartial bool
61
62 // UseProtoNames uses proto field name instead of lowerCamelCase name in JSON
63 // field names.
64 UseProtoNames bool
65
66 // UseEnumNumbers emits enum values as numbers.
67 UseEnumNumbers bool
68
69 // EmitUnpopulated specifies whether to emit unpopulated fields. It does not
70 // emit unpopulated oneof fields or unpopulated extension fields.
71 // The JSON value emitted for unpopulated fields are as follows:
72 // ╔═══════╤════════════════════════════╗
73 // ║ JSON │ Protobuf field ║
74 // ╠═══════╪════════════════════════════╣
75 // ║ false │ proto3 boolean fields ║
76 // ║ 0 │ proto3 numeric fields ║
77 // ║ "" │ proto3 string/bytes fields ║
78 // ║ null │ proto2 scalar fields ║
79 // ║ null │ message fields ║
80 // ║ [] │ list fields ║
81 // ║ {} │ map fields ║
82 // ╚═══════╧════════════════════════════╝
83 EmitUnpopulated bool
84
85 // Resolver is used for looking up types when expanding google.protobuf.Any
86 // messages. If nil, this defaults to using protoregistry.GlobalTypes.
87 Resolver interface {
88 protoregistry.ExtensionTypeResolver
89 protoregistry.MessageTypeResolver
90 }
91}
92
93// Format formats the message as a string.
94// This method is only intended for human consumption and ignores errors.
95// Do not depend on the output being stable. It may change over time across
96// different versions of the program.
97func (o MarshalOptions) Format(m proto.Message) string {
98 if m == nil || !m.ProtoReflect().IsValid() {
99 return "<nil>" // invalid syntax, but okay since this is for debugging
100 }
101 o.AllowPartial = true
102 b, _ := o.Marshal(m)
103 return string(b)
104}
105
106// Marshal marshals the given proto.Message in the JSON format using options in
107// MarshalOptions. Do not depend on the output being stable. It may change over
108// time across different versions of the program.
109func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
110 return o.marshal(m)
111}
112
113// marshal is a centralized function that all marshal operations go through.
114// For profiling purposes, avoid changing the name of this function or
115// introducing other code paths for marshal that do not go through this.
116func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) {
117 if o.Multiline && o.Indent == "" {
118 o.Indent = defaultIndent
119 }
120 if o.Resolver == nil {
121 o.Resolver = protoregistry.GlobalTypes
122 }
123
124 internalEnc, err := json.NewEncoder(o.Indent)
125 if err != nil {
126 return nil, err
127 }
128
129 // Treat nil message interface as an empty message,
130 // in which case the output in an empty JSON object.
131 if m == nil {
132 return []byte("{}"), nil
133 }
134
135 enc := encoder{internalEnc, o}
136 if err := enc.marshalMessage(m.ProtoReflect(), ""); err != nil {
137 return nil, err
138 }
139 if o.AllowPartial {
140 return enc.Bytes(), nil
141 }
142 return enc.Bytes(), proto.CheckInitialized(m)
143}
144
145type encoder struct {
146 *json.Encoder
147 opts MarshalOptions
148}
149
150// typeFieldDesc is a synthetic field descriptor used for the "@type" field.
151var typeFieldDesc = func() protoreflect.FieldDescriptor {
152 var fd filedesc.Field
153 fd.L0.FullName = "@type"
154 fd.L0.Index = -1
155 fd.L1.Cardinality = protoreflect.Optional
156 fd.L1.Kind = protoreflect.StringKind
157 return &fd
158}()
159
160// typeURLFieldRanger wraps a protoreflect.Message and modifies its Range method
161// to additionally iterate over a synthetic field for the type URL.
162type typeURLFieldRanger struct {
163 order.FieldRanger
164 typeURL string
165}
166
167func (m typeURLFieldRanger) Range(f func(pref.FieldDescriptor, pref.Value) bool) {
168 if !f(typeFieldDesc, pref.ValueOfString(m.typeURL)) {
169 return
170 }
171 m.FieldRanger.Range(f)
172}
173
174// unpopulatedFieldRanger wraps a protoreflect.Message and modifies its Range
175// method to additionally iterate over unpopulated fields.
176type unpopulatedFieldRanger struct{ pref.Message }
177
178func (m unpopulatedFieldRanger) Range(f func(pref.FieldDescriptor, pref.Value) bool) {
179 fds := m.Descriptor().Fields()
180 for i := 0; i < fds.Len(); i++ {
181 fd := fds.Get(i)
182 if m.Has(fd) || fd.ContainingOneof() != nil {
183 continue // ignore populated fields and fields within a oneofs
184 }
185
186 v := m.Get(fd)
187 isProto2Scalar := fd.Syntax() == pref.Proto2 && fd.Default().IsValid()
188 isSingularMessage := fd.Cardinality() != pref.Repeated && fd.Message() != nil
189 if isProto2Scalar || isSingularMessage {
190 v = pref.Value{} // use invalid value to emit null
191 }
192 if !f(fd, v) {
193 return
194 }
195 }
196 m.Message.Range(f)
197}
198
199// marshalMessage marshals the fields in the given protoreflect.Message.
200// If the typeURL is non-empty, then a synthetic "@type" field is injected
201// containing the URL as the value.
202func (e encoder) marshalMessage(m pref.Message, typeURL string) error {
203 if !flags.ProtoLegacy && messageset.IsMessageSet(m.Descriptor()) {
204 return errors.New("no support for proto1 MessageSets")
205 }
206
207 if marshal := wellKnownTypeMarshaler(m.Descriptor().FullName()); marshal != nil {
208 return marshal(e, m)
209 }
210
211 e.StartObject()
212 defer e.EndObject()
213
214 var fields order.FieldRanger = m
215 if e.opts.EmitUnpopulated {
216 fields = unpopulatedFieldRanger{m}
217 }
218 if typeURL != "" {
219 fields = typeURLFieldRanger{fields, typeURL}
220 }
221
222 var err error
223 order.RangeFields(fields, order.IndexNameFieldOrder, func(fd pref.FieldDescriptor, v pref.Value) bool {
224 name := fd.JSONName()
225 if e.opts.UseProtoNames {
226 name = fd.TextName()
227 }
228
229 if err = e.WriteName(name); err != nil {
230 return false
231 }
232 if err = e.marshalValue(v, fd); err != nil {
233 return false
234 }
235 return true
236 })
237 return err
238}
239
240// marshalValue marshals the given protoreflect.Value.
241func (e encoder) marshalValue(val pref.Value, fd pref.FieldDescriptor) error {
242 switch {
243 case fd.IsList():
244 return e.marshalList(val.List(), fd)
245 case fd.IsMap():
246 return e.marshalMap(val.Map(), fd)
247 default:
248 return e.marshalSingular(val, fd)
249 }
250}
251
252// marshalSingular marshals the given non-repeated field value. This includes
253// all scalar types, enums, messages, and groups.
254func (e encoder) marshalSingular(val pref.Value, fd pref.FieldDescriptor) error {
255 if !val.IsValid() {
256 e.WriteNull()
257 return nil
258 }
259
260 switch kind := fd.Kind(); kind {
261 case pref.BoolKind:
262 e.WriteBool(val.Bool())
263
264 case pref.StringKind:
265 if e.WriteString(val.String()) != nil {
266 return errors.InvalidUTF8(string(fd.FullName()))
267 }
268
269 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
270 e.WriteInt(val.Int())
271
272 case pref.Uint32Kind, pref.Fixed32Kind:
273 e.WriteUint(val.Uint())
274
275 case pref.Int64Kind, pref.Sint64Kind, pref.Uint64Kind,
276 pref.Sfixed64Kind, pref.Fixed64Kind:
277 // 64-bit integers are written out as JSON string.
278 e.WriteString(val.String())
279
280 case pref.FloatKind:
281 // Encoder.WriteFloat handles the special numbers NaN and infinites.
282 e.WriteFloat(val.Float(), 32)
283
284 case pref.DoubleKind:
285 // Encoder.WriteFloat handles the special numbers NaN and infinites.
286 e.WriteFloat(val.Float(), 64)
287
288 case pref.BytesKind:
289 e.WriteString(base64.StdEncoding.EncodeToString(val.Bytes()))
290
291 case pref.EnumKind:
292 if fd.Enum().FullName() == genid.NullValue_enum_fullname {
293 e.WriteNull()
294 } else {
295 desc := fd.Enum().Values().ByNumber(val.Enum())
296 if e.opts.UseEnumNumbers || desc == nil {
297 e.WriteInt(int64(val.Enum()))
298 } else {
299 e.WriteString(string(desc.Name()))
300 }
301 }
302
303 case pref.MessageKind, pref.GroupKind:
304 if err := e.marshalMessage(val.Message(), ""); err != nil {
305 return err
306 }
307
308 default:
309 panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind))
310 }
311 return nil
312}
313
314// marshalList marshals the given protoreflect.List.
315func (e encoder) marshalList(list pref.List, fd pref.FieldDescriptor) error {
316 e.StartArray()
317 defer e.EndArray()
318
319 for i := 0; i < list.Len(); i++ {
320 item := list.Get(i)
321 if err := e.marshalSingular(item, fd); err != nil {
322 return err
323 }
324 }
325 return nil
326}
327
328// marshalMap marshals given protoreflect.Map.
329func (e encoder) marshalMap(mmap pref.Map, fd pref.FieldDescriptor) error {
330 e.StartObject()
331 defer e.EndObject()
332
333 var err error
334 order.RangeEntries(mmap, order.GenericKeyOrder, func(k pref.MapKey, v pref.Value) bool {
335 if err = e.WriteName(k.String()); err != nil {
336 return false
337 }
338 if err = e.marshalSingular(v, fd.MapValue()); err != nil {
339 return false
340 }
341 return true
342 })
343 return err
344}