blob: 9b2592dd357f3d082fdea286af0309a50974b56a [file] [log] [blame]
Herbie Ong7b828bc2019-02-08 19:56:24 -08001// 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
Damien Neil5c5b5312019-05-14 12:44:37 -07005package protojson
Herbie Ong7b828bc2019-02-08 19:56:24 -08006
7import (
8 "encoding/base64"
Herbie Ong0b0f4032019-03-18 19:06:15 -07009 "fmt"
Herbie Ong7b828bc2019-02-08 19:56:24 -080010 "sort"
11
Damien Neile89e6242019-05-13 23:55:40 -070012 "google.golang.org/protobuf/internal/encoding/json"
Joe Tsai5ae10aa2019-07-11 18:23:08 -070013 "google.golang.org/protobuf/internal/encoding/messageset"
14 "google.golang.org/protobuf/internal/errors"
15 "google.golang.org/protobuf/internal/flags"
Damien Neile89e6242019-05-13 23:55:40 -070016 "google.golang.org/protobuf/internal/pragma"
17 "google.golang.org/protobuf/proto"
18 pref "google.golang.org/protobuf/reflect/protoreflect"
19 "google.golang.org/protobuf/reflect/protoregistry"
Herbie Ong7b828bc2019-02-08 19:56:24 -080020)
21
22// Marshal writes the given proto.Message in JSON format using default options.
Herbie Ong9e356de2019-08-05 11:40:38 +080023// Do not depend on the output being stable. It may change over time across
Herbie Ong582ab3d2019-09-06 15:56:09 -070024// different versions of the program.
Herbie Ong7b828bc2019-02-08 19:56:24 -080025func Marshal(m proto.Message) ([]byte, error) {
26 return MarshalOptions{}.Marshal(m)
27}
28
29// MarshalOptions is a configurable JSON format marshaler.
30type MarshalOptions struct {
31 pragma.NoUnkeyedLiterals
Herbie Ong984e5282019-09-06 00:29:48 -070032 encoder *json.Encoder
Herbie Ong7b828bc2019-02-08 19:56:24 -080033
Herbie Ong329be5b2019-03-27 14:47:59 -070034 // AllowPartial allows messages that have missing required fields to marshal
35 // without returning an error. If AllowPartial is false (the default),
36 // Marshal will return error if there are any missing required fields.
37 AllowPartial bool
38
Herbie Ong956cd6d2019-09-06 15:17:22 -070039 // UseProtoNames uses proto field name instead of lowerCamelCase name in JSON
40 // field names.
41 UseProtoNames bool
42
Herbie Ong9111f3b2019-09-06 14:35:09 -070043 // UseEnumNumbers emits enum values as numbers.
44 UseEnumNumbers bool
45
Herbie Ong984e5282019-09-06 00:29:48 -070046 // EmitUnpopulated specifies whether to emit unpopulated fields. It does not
47 // emit unpopulated oneof fields or unpopulated extension fields.
48 // The JSON value emitted for unpopulated fields are as follows:
Herbie Ong956cd6d2019-09-06 15:17:22 -070049 // ╔═══════╤════════════════════════════╗
50 // ║ JSON │ Protobuf field ║
51 // ╠═══════╪════════════════════════════╣
52 // ║ false │ proto3 boolean fields ║
53 // ║ 0 │ proto3 numeric fields ║
54 // ║ "" │ proto3 string/bytes fields ║
55 // ║ null │ proto2 scalar fields ║
56 // ║ null │ message fields ║
57 // ║ [] │ list fields ║
58 // ║ {} │ map fields ║
59 // ╚═══════╧════════════════════════════╝
Herbie Ong984e5282019-09-06 00:29:48 -070060 EmitUnpopulated bool
61
Herbie Ong0b0f4032019-03-18 19:06:15 -070062 // If Indent is a non-empty string, it causes entries for an Array or Object
63 // to be preceded by the indent and trailed by a newline. Indent can only be
64 // composed of space or tab characters.
65 Indent string
66
Joe Tsai1c283042019-05-14 14:28:19 -070067 // Resolver is used for looking up types when expanding google.protobuf.Any
68 // messages. If nil, this defaults to using protoregistry.GlobalTypes.
69 Resolver interface {
Damien Neil95faac22019-06-19 10:03:37 -070070 protoregistry.ExtensionTypeResolver
Joe Tsai1c283042019-05-14 14:28:19 -070071 protoregistry.MessageTypeResolver
72 }
Herbie Ong7b828bc2019-02-08 19:56:24 -080073}
74
Herbie Ong0b0f4032019-03-18 19:06:15 -070075// Marshal marshals the given proto.Message in the JSON format using options in
Herbie Ong582ab3d2019-09-06 15:56:09 -070076// MarshalOptions. Do not depend on the output being stable. It may change over
77// time across different versions of the program.
Herbie Ong7b828bc2019-02-08 19:56:24 -080078func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
Herbie Ong822de2d2019-03-27 13:16:23 -070079 var err error
80 o.encoder, err = json.NewEncoder(o.Indent)
Herbie Ong87608a72019-03-06 14:32:24 -080081 if err != nil {
82 return nil, err
83 }
Herbie Ong822de2d2019-03-27 13:16:23 -070084 if o.Resolver == nil {
85 o.Resolver = protoregistry.GlobalTypes
86 }
Herbie Ong87608a72019-03-06 14:32:24 -080087
Herbie Ong822de2d2019-03-27 13:16:23 -070088 err = o.marshalMessage(m.ProtoReflect())
Damien Neil8c86fc52019-06-19 09:28:29 -070089 if err != nil {
Herbie Ong7b828bc2019-02-08 19:56:24 -080090 return nil, err
91 }
Damien Neil8c86fc52019-06-19 09:28:29 -070092 if o.AllowPartial {
93 return o.encoder.Bytes(), nil
Damien Neil4686e232019-04-05 13:31:40 -070094 }
Damien Neil8c86fc52019-06-19 09:28:29 -070095 return o.encoder.Bytes(), proto.IsInitialized(m)
Herbie Ong87608a72019-03-06 14:32:24 -080096}
97
98// marshalMessage marshals the given protoreflect.Message.
Herbie Ong822de2d2019-03-27 13:16:23 -070099func (o MarshalOptions) marshalMessage(m pref.Message) error {
Joe Tsai0fc49f82019-05-01 12:29:25 -0700100 if isCustomType(m.Descriptor().FullName()) {
Herbie Ong822de2d2019-03-27 13:16:23 -0700101 return o.marshalCustomType(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -0700102 }
103
Herbie Ong822de2d2019-03-27 13:16:23 -0700104 o.encoder.StartObject()
105 defer o.encoder.EndObject()
Damien Neil8c86fc52019-06-19 09:28:29 -0700106 if err := o.marshalFields(m); err != nil {
Herbie Ong0b0f4032019-03-18 19:06:15 -0700107 return err
108 }
Herbie Ong87608a72019-03-06 14:32:24 -0800109
Damien Neil8c86fc52019-06-19 09:28:29 -0700110 return nil
Herbie Ong0b0f4032019-03-18 19:06:15 -0700111}
112
113// marshalFields marshals the fields in the given protoreflect.Message.
Herbie Ong822de2d2019-03-27 13:16:23 -0700114func (o MarshalOptions) marshalFields(m pref.Message) error {
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700115 messageDesc := m.Descriptor()
Joe Tsai1799d112019-08-08 13:31:59 -0700116 if !flags.ProtoLegacy && messageset.IsMessageSet(messageDesc) {
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700117 return errors.New("no support for proto1 MessageSets")
118 }
119
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700120 // Marshal out known fields.
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700121 fieldDescs := messageDesc.Fields()
Joe Tsaifc5f8c32019-09-17 22:32:53 -0700122 for i := 0; i < fieldDescs.Len(); {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800123 fd := fieldDescs.Get(i)
Joe Tsaifc5f8c32019-09-17 22:32:53 -0700124 if od := fd.ContainingOneof(); od != nil {
125 fd = m.WhichOneof(od)
126 i += od.Fields().Len()
127 if fd == nil {
128 continue // unpopulated oneofs are not affected by EmitUnpopulated
129 }
130 } else {
131 i++
132 }
133
Herbie Ong984e5282019-09-06 00:29:48 -0700134 val := m.Get(fd)
Joe Tsai378c1322019-04-25 23:48:08 -0700135 if !m.Has(fd) {
Joe Tsaifc5f8c32019-09-17 22:32:53 -0700136 if !o.EmitUnpopulated {
Herbie Ong984e5282019-09-06 00:29:48 -0700137 continue
138 }
139 isProto2Scalar := fd.Syntax() == pref.Proto2 && fd.Default().IsValid()
140 isSingularMessage := fd.Cardinality() != pref.Repeated && fd.Message() != nil
141 if isProto2Scalar || isSingularMessage {
142 // Use invalid value to emit null.
143 val = pref.Value{}
144 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800145 }
146
Herbie Ong87608a72019-03-06 14:32:24 -0800147 name := fd.JSONName()
Herbie Ong956cd6d2019-09-06 15:17:22 -0700148 if o.UseProtoNames {
149 name = string(fd.Name())
150 // Use type name for group field name.
151 if fd.Kind() == pref.GroupKind {
152 name = string(fd.Message().Name())
153 }
154 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700155 if err := o.encoder.WriteName(name); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800156 return err
157 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700158 if err := o.marshalValue(val, fd); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800159 return err
Herbie Ong7b828bc2019-02-08 19:56:24 -0800160 }
161 }
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700162
163 // Marshal out extensions.
Damien Neil8c86fc52019-06-19 09:28:29 -0700164 if err := o.marshalExtensions(m); err != nil {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700165 return err
166 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700167 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800168}
169
Herbie Ong87608a72019-03-06 14:32:24 -0800170// marshalValue marshals the given protoreflect.Value.
Herbie Ong822de2d2019-03-27 13:16:23 -0700171func (o MarshalOptions) marshalValue(val pref.Value, fd pref.FieldDescriptor) error {
Joe Tsaiac31a352019-05-13 14:32:56 -0700172 switch {
173 case fd.IsList():
174 return o.marshalList(val.List(), fd)
175 case fd.IsMap():
176 return o.marshalMap(val.Map(), fd)
177 default:
178 return o.marshalSingular(val, fd)
Herbie Ong7b828bc2019-02-08 19:56:24 -0800179 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800180}
181
Herbie Ong87608a72019-03-06 14:32:24 -0800182// marshalSingular marshals the given non-repeated field value. This includes
183// all scalar types, enums, messages, and groups.
Herbie Ong822de2d2019-03-27 13:16:23 -0700184func (o MarshalOptions) marshalSingular(val pref.Value, fd pref.FieldDescriptor) error {
Herbie Ong984e5282019-09-06 00:29:48 -0700185 if !val.IsValid() {
186 o.encoder.WriteNull()
187 return nil
188 }
189
Herbie Ong87608a72019-03-06 14:32:24 -0800190 switch kind := fd.Kind(); kind {
191 case pref.BoolKind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700192 o.encoder.WriteBool(val.Bool())
Herbie Ong87608a72019-03-06 14:32:24 -0800193
194 case pref.StringKind:
Damien Neil8c86fc52019-06-19 09:28:29 -0700195 if err := o.encoder.WriteString(val.String()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800196 return err
197 }
198
199 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700200 o.encoder.WriteInt(val.Int())
Herbie Ong87608a72019-03-06 14:32:24 -0800201
202 case pref.Uint32Kind, pref.Fixed32Kind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700203 o.encoder.WriteUint(val.Uint())
Herbie Ong7b828bc2019-02-08 19:56:24 -0800204
205 case pref.Int64Kind, pref.Sint64Kind, pref.Uint64Kind,
206 pref.Sfixed64Kind, pref.Fixed64Kind:
Herbie Ong87608a72019-03-06 14:32:24 -0800207 // 64-bit integers are written out as JSON string.
Herbie Ong822de2d2019-03-27 13:16:23 -0700208 o.encoder.WriteString(val.String())
Herbie Ong7b828bc2019-02-08 19:56:24 -0800209
Herbie Ong87608a72019-03-06 14:32:24 -0800210 case pref.FloatKind:
211 // Encoder.WriteFloat handles the special numbers NaN and infinites.
Herbie Ong822de2d2019-03-27 13:16:23 -0700212 o.encoder.WriteFloat(val.Float(), 32)
Herbie Ong87608a72019-03-06 14:32:24 -0800213
214 case pref.DoubleKind:
215 // Encoder.WriteFloat handles the special numbers NaN and infinites.
Herbie Ong822de2d2019-03-27 13:16:23 -0700216 o.encoder.WriteFloat(val.Float(), 64)
Herbie Ong7b828bc2019-02-08 19:56:24 -0800217
218 case pref.BytesKind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700219 err := o.encoder.WriteString(base64.StdEncoding.EncodeToString(val.Bytes()))
Damien Neil8c86fc52019-06-19 09:28:29 -0700220 if err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800221 return err
222 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800223
224 case pref.EnumKind:
Joe Tsaid24bc722019-04-15 23:39:09 -0700225 if fd.Enum().FullName() == "google.protobuf.NullValue" {
Herbie Ong822de2d2019-03-27 13:16:23 -0700226 o.encoder.WriteNull()
Herbie Ong87608a72019-03-06 14:32:24 -0800227 } else {
Herbie Ong9111f3b2019-09-06 14:35:09 -0700228 desc := fd.Enum().Values().ByNumber(val.Enum())
229 if o.UseEnumNumbers || desc == nil {
230 o.encoder.WriteInt(int64(val.Enum()))
231 } else {
232 err := o.encoder.WriteString(string(desc.Name()))
233 if err != nil {
234 return err
235 }
236 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800237 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800238
239 case pref.MessageKind, pref.GroupKind:
Damien Neil8c86fc52019-06-19 09:28:29 -0700240 if err := o.marshalMessage(val.Message()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800241 return err
242 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800243
Herbie Ong87608a72019-03-06 14:32:24 -0800244 default:
Herbie Ong0b0f4032019-03-18 19:06:15 -0700245 panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind))
Herbie Ong87608a72019-03-06 14:32:24 -0800246 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700247 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800248}
249
Herbie Ong87608a72019-03-06 14:32:24 -0800250// marshalList marshals the given protoreflect.List.
Herbie Ong822de2d2019-03-27 13:16:23 -0700251func (o MarshalOptions) marshalList(list pref.List, fd pref.FieldDescriptor) error {
252 o.encoder.StartArray()
253 defer o.encoder.EndArray()
Herbie Ong87608a72019-03-06 14:32:24 -0800254
Herbie Ong87608a72019-03-06 14:32:24 -0800255 for i := 0; i < list.Len(); i++ {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800256 item := list.Get(i)
Damien Neil8c86fc52019-06-19 09:28:29 -0700257 if err := o.marshalSingular(item, fd); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800258 return err
Herbie Ong7b828bc2019-02-08 19:56:24 -0800259 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800260 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700261 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800262}
263
264type mapEntry struct {
265 key pref.MapKey
266 value pref.Value
267}
268
Herbie Ong87608a72019-03-06 14:32:24 -0800269// marshalMap marshals given protoreflect.Map.
Herbie Ong822de2d2019-03-27 13:16:23 -0700270func (o MarshalOptions) marshalMap(mmap pref.Map, fd pref.FieldDescriptor) error {
271 o.encoder.StartObject()
272 defer o.encoder.EndObject()
Herbie Ong87608a72019-03-06 14:32:24 -0800273
Herbie Ong7b828bc2019-02-08 19:56:24 -0800274 // Get a sorted list based on keyType first.
275 entries := make([]mapEntry, 0, mmap.Len())
276 mmap.Range(func(key pref.MapKey, val pref.Value) bool {
277 entries = append(entries, mapEntry{key: key, value: val})
278 return true
279 })
Joe Tsaiac31a352019-05-13 14:32:56 -0700280 sortMap(fd.MapKey().Kind(), entries)
Herbie Ong7b828bc2019-02-08 19:56:24 -0800281
Herbie Ong87608a72019-03-06 14:32:24 -0800282 // Write out sorted list.
Herbie Ong7b828bc2019-02-08 19:56:24 -0800283 for _, entry := range entries {
Damien Neil8c86fc52019-06-19 09:28:29 -0700284 if err := o.encoder.WriteName(entry.key.String()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800285 return err
Herbie Ong7b828bc2019-02-08 19:56:24 -0800286 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700287 if err := o.marshalSingular(entry.value, fd.MapValue()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800288 return err
289 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800290 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700291 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800292}
293
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700294// sortMap orders list based on value of key field for deterministic ordering.
Herbie Ong7b828bc2019-02-08 19:56:24 -0800295func sortMap(keyKind pref.Kind, values []mapEntry) {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700296 sort.Slice(values, func(i, j int) bool {
297 switch keyKind {
298 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind,
299 pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
Herbie Ong7b828bc2019-02-08 19:56:24 -0800300 return values[i].key.Int() < values[j].key.Int()
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700301
302 case pref.Uint32Kind, pref.Fixed32Kind,
303 pref.Uint64Kind, pref.Fixed64Kind:
Herbie Ong7b828bc2019-02-08 19:56:24 -0800304 return values[i].key.Uint() < values[j].key.Uint()
305 }
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700306 return values[i].key.String() < values[j].key.String()
307 })
308}
309
310// marshalExtensions marshals extension fields.
Joe Tsai378c1322019-04-25 23:48:08 -0700311func (o MarshalOptions) marshalExtensions(m pref.Message) error {
312 type entry struct {
313 key string
314 value pref.Value
315 desc pref.FieldDescriptor
Herbie Ong7b828bc2019-02-08 19:56:24 -0800316 }
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700317
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700318 // Get a sorted list based on field key first.
Joe Tsai378c1322019-04-25 23:48:08 -0700319 var entries []entry
320 m.Range(func(fd pref.FieldDescriptor, v pref.Value) bool {
321 if !fd.IsExtension() {
322 return true
323 }
Joe Tsai378c1322019-04-25 23:48:08 -0700324
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700325 // For MessageSet extensions, the name used is the parent message.
Joe Tsaid4211502019-07-02 14:58:02 -0700326 name := fd.FullName()
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700327 if messageset.IsMessageSetExtension(fd) {
328 name = name.Parent()
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700329 }
330
Joe Tsai378c1322019-04-25 23:48:08 -0700331 // Use [name] format for JSON field name.
332 entries = append(entries, entry{
333 key: string(name),
334 value: v,
335 desc: fd,
336 })
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700337 return true
338 })
339
340 // Sort extensions lexicographically.
341 sort.Slice(entries, func(i, j int) bool {
342 return entries[i].key < entries[j].key
343 })
344
345 // Write out sorted list.
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700346 for _, entry := range entries {
347 // JSON field name is the proto field name enclosed in [], similar to
348 // textproto. This is consistent with Go v1 lib. C++ lib v3.7.0 does not
349 // marshal out extension fields.
Damien Neil8c86fc52019-06-19 09:28:29 -0700350 if err := o.encoder.WriteName("[" + entry.key + "]"); err != nil {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700351 return err
352 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700353 if err := o.marshalValue(entry.value, entry.desc); err != nil {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700354 return err
355 }
356 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700357 return nil
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700358}