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