blob: 0e72d85378b31d982abf7cdc9be8255f1a25177e [file] [log] [blame]
khenaidoof3333552021-12-15 16:52:31 -05001// 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"
khenaidoof3333552021-12-15 16:52:31 -050021 "google.golang.org/protobuf/reflect/protoregistry"
22)
23
24const defaultIndent = " "
25
26// Format formats the message as a multiline string.
27// This function is only intended for human consumption and ignores errors.
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +053028// Do not depend on the output being stable. Its output will change across
29// different builds of your program, even when using the same version of the
30// protobuf module.
khenaidoof3333552021-12-15 16:52:31 -050031func Format(m proto.Message) string {
32 return MarshalOptions{Multiline: true}.Format(m)
33}
34
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +053035// Marshal writes the given [proto.Message] in JSON format using default options.
36// Do not depend on the output being stable. Its output will change across
37// different builds of your program, even when using the same version of the
38// protobuf module.
khenaidoof3333552021-12-15 16:52:31 -050039func Marshal(m proto.Message) ([]byte, error) {
40 return MarshalOptions{}.Marshal(m)
41}
42
43// MarshalOptions is a configurable JSON format marshaler.
44type MarshalOptions struct {
45 pragma.NoUnkeyedLiterals
46
47 // Multiline specifies whether the marshaler should format the output in
48 // indented-form with every textual element on a new line.
49 // If Indent is an empty string, then an arbitrary indent is chosen.
50 Multiline bool
51
52 // Indent specifies the set of indentation characters to use in a multiline
53 // formatted output such that every entry is preceded by Indent and
54 // terminated by a newline. If non-empty, then Multiline is treated as true.
55 // Indent can only be composed of space or tab characters.
56 Indent string
57
58 // AllowPartial allows messages that have missing required fields to marshal
59 // without returning an error. If AllowPartial is false (the default),
60 // Marshal will return error if there are any missing required fields.
61 AllowPartial bool
62
63 // UseProtoNames uses proto field name instead of lowerCamelCase name in JSON
64 // field names.
65 UseProtoNames bool
66
67 // UseEnumNumbers emits enum values as numbers.
68 UseEnumNumbers bool
69
70 // EmitUnpopulated specifies whether to emit unpopulated fields. It does not
71 // emit unpopulated oneof fields or unpopulated extension fields.
72 // The JSON value emitted for unpopulated fields are as follows:
73 // ╔═══════╤════════════════════════════╗
74 // ║ JSON │ Protobuf field ║
75 // ╠═══════╪════════════════════════════╣
76 // ║ false │ proto3 boolean fields ║
77 // ║ 0 │ proto3 numeric fields ║
78 // ║ "" │ proto3 string/bytes fields ║
79 // ║ null │ proto2 scalar fields ║
80 // ║ null │ message fields ║
81 // ║ [] │ list fields ║
82 // ║ {} │ map fields ║
83 // ╚═══════╧════════════════════════════╝
84 EmitUnpopulated bool
85
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +053086 // EmitDefaultValues specifies whether to emit default-valued primitive fields,
87 // empty lists, and empty maps. The fields affected are as follows:
88 // ╔═══════╤════════════════════════════════════════╗
89 // ║ JSON │ Protobuf field ║
90 // ╠═══════╪════════════════════════════════════════╣
91 // ║ false │ non-optional scalar boolean fields ║
92 // ║ 0 │ non-optional scalar numeric fields ║
93 // ║ "" │ non-optional scalar string/byte fields ║
94 // ║ [] │ empty repeated fields ║
95 // ║ {} │ empty map fields ║
96 // ╚═══════╧════════════════════════════════════════╝
97 //
98 // Behaves similarly to EmitUnpopulated, but does not emit "null"-value fields,
99 // i.e. presence-sensing fields that are omitted will remain omitted to preserve
100 // presence-sensing.
101 // EmitUnpopulated takes precedence over EmitDefaultValues since the former generates
102 // a strict superset of the latter.
103 EmitDefaultValues bool
104
khenaidoof3333552021-12-15 16:52:31 -0500105 // Resolver is used for looking up types when expanding google.protobuf.Any
106 // messages. If nil, this defaults to using protoregistry.GlobalTypes.
107 Resolver interface {
108 protoregistry.ExtensionTypeResolver
109 protoregistry.MessageTypeResolver
110 }
111}
112
113// Format formats the message as a string.
114// This method is only intended for human consumption and ignores errors.
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530115// Do not depend on the output being stable. Its output will change across
116// different builds of your program, even when using the same version of the
117// protobuf module.
khenaidoof3333552021-12-15 16:52:31 -0500118func (o MarshalOptions) Format(m proto.Message) string {
119 if m == nil || !m.ProtoReflect().IsValid() {
120 return "<nil>" // invalid syntax, but okay since this is for debugging
121 }
122 o.AllowPartial = true
123 b, _ := o.Marshal(m)
124 return string(b)
125}
126
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530127// Marshal marshals the given [proto.Message] in the JSON format using options in
128// Do not depend on the output being stable. Its output will change across
129// different builds of your program, even when using the same version of the
130// protobuf module.
khenaidoof3333552021-12-15 16:52:31 -0500131func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530132 return o.marshal(nil, m)
133}
134
135// MarshalAppend appends the JSON format encoding of m to b,
136// returning the result.
137func (o MarshalOptions) MarshalAppend(b []byte, m proto.Message) ([]byte, error) {
138 return o.marshal(b, m)
khenaidoof3333552021-12-15 16:52:31 -0500139}
140
141// marshal is a centralized function that all marshal operations go through.
142// For profiling purposes, avoid changing the name of this function or
143// introducing other code paths for marshal that do not go through this.
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530144func (o MarshalOptions) marshal(b []byte, m proto.Message) ([]byte, error) {
khenaidoof3333552021-12-15 16:52:31 -0500145 if o.Multiline && o.Indent == "" {
146 o.Indent = defaultIndent
147 }
148 if o.Resolver == nil {
149 o.Resolver = protoregistry.GlobalTypes
150 }
151
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530152 internalEnc, err := json.NewEncoder(b, o.Indent)
khenaidoof3333552021-12-15 16:52:31 -0500153 if err != nil {
154 return nil, err
155 }
156
157 // Treat nil message interface as an empty message,
158 // in which case the output in an empty JSON object.
159 if m == nil {
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530160 return append(b, '{', '}'), nil
khenaidoof3333552021-12-15 16:52:31 -0500161 }
162
163 enc := encoder{internalEnc, o}
164 if err := enc.marshalMessage(m.ProtoReflect(), ""); err != nil {
165 return nil, err
166 }
167 if o.AllowPartial {
168 return enc.Bytes(), nil
169 }
170 return enc.Bytes(), proto.CheckInitialized(m)
171}
172
173type encoder struct {
174 *json.Encoder
175 opts MarshalOptions
176}
177
178// typeFieldDesc is a synthetic field descriptor used for the "@type" field.
179var typeFieldDesc = func() protoreflect.FieldDescriptor {
180 var fd filedesc.Field
181 fd.L0.FullName = "@type"
182 fd.L0.Index = -1
183 fd.L1.Cardinality = protoreflect.Optional
184 fd.L1.Kind = protoreflect.StringKind
185 return &fd
186}()
187
188// typeURLFieldRanger wraps a protoreflect.Message and modifies its Range method
189// to additionally iterate over a synthetic field for the type URL.
190type typeURLFieldRanger struct {
191 order.FieldRanger
192 typeURL string
193}
194
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530195func (m typeURLFieldRanger) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) {
196 if !f(typeFieldDesc, protoreflect.ValueOfString(m.typeURL)) {
khenaidoof3333552021-12-15 16:52:31 -0500197 return
198 }
199 m.FieldRanger.Range(f)
200}
201
202// unpopulatedFieldRanger wraps a protoreflect.Message and modifies its Range
203// method to additionally iterate over unpopulated fields.
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530204type unpopulatedFieldRanger struct {
205 protoreflect.Message
khenaidoof3333552021-12-15 16:52:31 -0500206
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530207 skipNull bool
208}
209
210func (m unpopulatedFieldRanger) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) {
khenaidoof3333552021-12-15 16:52:31 -0500211 fds := m.Descriptor().Fields()
212 for i := 0; i < fds.Len(); i++ {
213 fd := fds.Get(i)
214 if m.Has(fd) || fd.ContainingOneof() != nil {
215 continue // ignore populated fields and fields within a oneofs
216 }
217
218 v := m.Get(fd)
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530219 if fd.HasPresence() {
220 if m.skipNull {
221 continue
222 }
223 v = protoreflect.Value{} // use invalid value to emit null
khenaidoof3333552021-12-15 16:52:31 -0500224 }
225 if !f(fd, v) {
226 return
227 }
228 }
229 m.Message.Range(f)
230}
231
232// marshalMessage marshals the fields in the given protoreflect.Message.
233// If the typeURL is non-empty, then a synthetic "@type" field is injected
234// containing the URL as the value.
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530235func (e encoder) marshalMessage(m protoreflect.Message, typeURL string) error {
khenaidoof3333552021-12-15 16:52:31 -0500236 if !flags.ProtoLegacy && messageset.IsMessageSet(m.Descriptor()) {
237 return errors.New("no support for proto1 MessageSets")
238 }
239
240 if marshal := wellKnownTypeMarshaler(m.Descriptor().FullName()); marshal != nil {
241 return marshal(e, m)
242 }
243
244 e.StartObject()
245 defer e.EndObject()
246
247 var fields order.FieldRanger = m
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530248 switch {
249 case e.opts.EmitUnpopulated:
250 fields = unpopulatedFieldRanger{Message: m, skipNull: false}
251 case e.opts.EmitDefaultValues:
252 fields = unpopulatedFieldRanger{Message: m, skipNull: true}
khenaidoof3333552021-12-15 16:52:31 -0500253 }
254 if typeURL != "" {
255 fields = typeURLFieldRanger{fields, typeURL}
256 }
257
258 var err error
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530259 order.RangeFields(fields, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
khenaidoof3333552021-12-15 16:52:31 -0500260 name := fd.JSONName()
261 if e.opts.UseProtoNames {
262 name = fd.TextName()
263 }
264
265 if err = e.WriteName(name); err != nil {
266 return false
267 }
268 if err = e.marshalValue(v, fd); err != nil {
269 return false
270 }
271 return true
272 })
273 return err
274}
275
276// marshalValue marshals the given protoreflect.Value.
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530277func (e encoder) marshalValue(val protoreflect.Value, fd protoreflect.FieldDescriptor) error {
khenaidoof3333552021-12-15 16:52:31 -0500278 switch {
279 case fd.IsList():
280 return e.marshalList(val.List(), fd)
281 case fd.IsMap():
282 return e.marshalMap(val.Map(), fd)
283 default:
284 return e.marshalSingular(val, fd)
285 }
286}
287
288// marshalSingular marshals the given non-repeated field value. This includes
289// all scalar types, enums, messages, and groups.
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530290func (e encoder) marshalSingular(val protoreflect.Value, fd protoreflect.FieldDescriptor) error {
khenaidoof3333552021-12-15 16:52:31 -0500291 if !val.IsValid() {
292 e.WriteNull()
293 return nil
294 }
295
296 switch kind := fd.Kind(); kind {
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530297 case protoreflect.BoolKind:
khenaidoof3333552021-12-15 16:52:31 -0500298 e.WriteBool(val.Bool())
299
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530300 case protoreflect.StringKind:
khenaidoof3333552021-12-15 16:52:31 -0500301 if e.WriteString(val.String()) != nil {
302 return errors.InvalidUTF8(string(fd.FullName()))
303 }
304
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530305 case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
khenaidoof3333552021-12-15 16:52:31 -0500306 e.WriteInt(val.Int())
307
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530308 case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
khenaidoof3333552021-12-15 16:52:31 -0500309 e.WriteUint(val.Uint())
310
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530311 case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind,
312 protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind:
khenaidoof3333552021-12-15 16:52:31 -0500313 // 64-bit integers are written out as JSON string.
314 e.WriteString(val.String())
315
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530316 case protoreflect.FloatKind:
khenaidoof3333552021-12-15 16:52:31 -0500317 // Encoder.WriteFloat handles the special numbers NaN and infinites.
318 e.WriteFloat(val.Float(), 32)
319
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530320 case protoreflect.DoubleKind:
khenaidoof3333552021-12-15 16:52:31 -0500321 // Encoder.WriteFloat handles the special numbers NaN and infinites.
322 e.WriteFloat(val.Float(), 64)
323
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530324 case protoreflect.BytesKind:
khenaidoof3333552021-12-15 16:52:31 -0500325 e.WriteString(base64.StdEncoding.EncodeToString(val.Bytes()))
326
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530327 case protoreflect.EnumKind:
khenaidoof3333552021-12-15 16:52:31 -0500328 if fd.Enum().FullName() == genid.NullValue_enum_fullname {
329 e.WriteNull()
330 } else {
331 desc := fd.Enum().Values().ByNumber(val.Enum())
332 if e.opts.UseEnumNumbers || desc == nil {
333 e.WriteInt(int64(val.Enum()))
334 } else {
335 e.WriteString(string(desc.Name()))
336 }
337 }
338
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530339 case protoreflect.MessageKind, protoreflect.GroupKind:
khenaidoof3333552021-12-15 16:52:31 -0500340 if err := e.marshalMessage(val.Message(), ""); err != nil {
341 return err
342 }
343
344 default:
345 panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind))
346 }
347 return nil
348}
349
350// marshalList marshals the given protoreflect.List.
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530351func (e encoder) marshalList(list protoreflect.List, fd protoreflect.FieldDescriptor) error {
khenaidoof3333552021-12-15 16:52:31 -0500352 e.StartArray()
353 defer e.EndArray()
354
355 for i := 0; i < list.Len(); i++ {
356 item := list.Get(i)
357 if err := e.marshalSingular(item, fd); err != nil {
358 return err
359 }
360 }
361 return nil
362}
363
364// marshalMap marshals given protoreflect.Map.
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530365func (e encoder) marshalMap(mmap protoreflect.Map, fd protoreflect.FieldDescriptor) error {
khenaidoof3333552021-12-15 16:52:31 -0500366 e.StartObject()
367 defer e.EndObject()
368
369 var err error
Akash Reddy Kankanala92dfdf82025-03-23 22:07:09 +0530370 order.RangeEntries(mmap, order.GenericKeyOrder, func(k protoreflect.MapKey, v protoreflect.Value) bool {
khenaidoof3333552021-12-15 16:52:31 -0500371 if err = e.WriteName(k.String()); err != nil {
372 return false
373 }
374 if err = e.marshalSingular(v, fd.MapValue()); err != nil {
375 return false
376 }
377 return true
378 })
379 return err
380}