encode.go

  1// 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	"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.
 28// 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.
 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. Its output will change across
 37// different builds of your program, even when using the same version of the
 38// protobuf module.
 39func 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
 86	// 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
105	// 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.
115// 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.
118func (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
127// 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.
131func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
132	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)
139}
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.
144func (o MarshalOptions) marshal(b []byte, m proto.Message) ([]byte, error) {
145	if o.Multiline && o.Indent == "" {
146		o.Indent = defaultIndent
147	}
148	if o.Resolver == nil {
149		o.Resolver = protoregistry.GlobalTypes
150	}
151
152	internalEnc, err := json.NewEncoder(b, o.Indent)
153	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 {
160		return append(b, '{', '}'), nil
161	}
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
195func (m typeURLFieldRanger) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) {
196	if !f(typeFieldDesc, protoreflect.ValueOfString(m.typeURL)) {
197		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.
204type unpopulatedFieldRanger struct {
205	protoreflect.Message
206
207	skipNull bool
208}
209
210func (m unpopulatedFieldRanger) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) {
211	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)
219		if fd.HasPresence() {
220			if m.skipNull {
221				continue
222			}
223			v = protoreflect.Value{} // use invalid value to emit null
224		}
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.
235func (e encoder) marshalMessage(m protoreflect.Message, typeURL string) error {
236	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
248	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}
253	}
254	if typeURL != "" {
255		fields = typeURLFieldRanger{fields, typeURL}
256	}
257
258	var err error
259	order.RangeFields(fields, order.IndexNameFieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
260		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.
277func (e encoder) marshalValue(val protoreflect.Value, fd protoreflect.FieldDescriptor) error {
278	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.
290func (e encoder) marshalSingular(val protoreflect.Value, fd protoreflect.FieldDescriptor) error {
291	if !val.IsValid() {
292		e.WriteNull()
293		return nil
294	}
295
296	switch kind := fd.Kind(); kind {
297	case protoreflect.BoolKind:
298		e.WriteBool(val.Bool())
299
300	case protoreflect.StringKind:
301		if e.WriteString(val.String()) != nil {
302			return errors.InvalidUTF8(string(fd.FullName()))
303		}
304
305	case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
306		e.WriteInt(val.Int())
307
308	case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
309		e.WriteUint(val.Uint())
310
311	case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind,
312		protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind:
313		// 64-bit integers are written out as JSON string.
314		e.WriteString(val.String())
315
316	case protoreflect.FloatKind:
317		// Encoder.WriteFloat handles the special numbers NaN and infinites.
318		e.WriteFloat(val.Float(), 32)
319
320	case protoreflect.DoubleKind:
321		// Encoder.WriteFloat handles the special numbers NaN and infinites.
322		e.WriteFloat(val.Float(), 64)
323
324	case protoreflect.BytesKind:
325		e.WriteString(base64.StdEncoding.EncodeToString(val.Bytes()))
326
327	case protoreflect.EnumKind:
328		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
339	case protoreflect.MessageKind, protoreflect.GroupKind:
340		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.
351func (e encoder) marshalList(list protoreflect.List, fd protoreflect.FieldDescriptor) error {
352	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.
365func (e encoder) marshalMap(mmap protoreflect.Map, fd protoreflect.FieldDescriptor) error {
366	e.StartObject()
367	defer e.EndObject()
368
369	var err error
370	order.RangeEntries(mmap, order.GenericKeyOrder, func(k protoreflect.MapKey, v protoreflect.Value) bool {
371		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}