blob: 02723c04f1748d132c2042e7c603cf2a3e0afa46 [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
Joe Tsai74b14602020-01-06 15:44:09 -080022const defaultIndent = " "
23
24// Format formats the message as a multiline string.
25// This function is only intended for human consumption and ignores errors.
26// Do not depend on the output being stable. It may change over time across
27// different versions of the program.
28func Format(m proto.Message) string {
29 return MarshalOptions{Multiline: true}.Format(m)
30}
31
Herbie Ong7b828bc2019-02-08 19:56:24 -080032// Marshal writes the given proto.Message in JSON format using default options.
Herbie Ong9e356de2019-08-05 11:40:38 +080033// Do not depend on the output being stable. It may change over time across
Herbie Ong582ab3d2019-09-06 15:56:09 -070034// different versions of the program.
Herbie Ong7b828bc2019-02-08 19:56:24 -080035func Marshal(m proto.Message) ([]byte, error) {
36 return MarshalOptions{}.Marshal(m)
37}
38
39// MarshalOptions is a configurable JSON format marshaler.
40type MarshalOptions struct {
41 pragma.NoUnkeyedLiterals
Herbie Ong984e5282019-09-06 00:29:48 -070042 encoder *json.Encoder
Herbie Ong7b828bc2019-02-08 19:56:24 -080043
Herbie Ong329be5b2019-03-27 14:47:59 -070044 // AllowPartial allows messages that have missing required fields to marshal
45 // without returning an error. If AllowPartial is false (the default),
46 // Marshal will return error if there are any missing required fields.
47 AllowPartial bool
48
Herbie Ong956cd6d2019-09-06 15:17:22 -070049 // UseProtoNames uses proto field name instead of lowerCamelCase name in JSON
50 // field names.
51 UseProtoNames bool
52
Herbie Ong9111f3b2019-09-06 14:35:09 -070053 // UseEnumNumbers emits enum values as numbers.
54 UseEnumNumbers bool
55
Herbie Ong984e5282019-09-06 00:29:48 -070056 // EmitUnpopulated specifies whether to emit unpopulated fields. It does not
57 // emit unpopulated oneof fields or unpopulated extension fields.
58 // The JSON value emitted for unpopulated fields are as follows:
Herbie Ong956cd6d2019-09-06 15:17:22 -070059 // ╔═══════╤════════════════════════════╗
60 // ║ JSON │ Protobuf field ║
61 // ╠═══════╪════════════════════════════╣
62 // ║ false │ proto3 boolean fields ║
63 // ║ 0 │ proto3 numeric fields ║
64 // ║ "" │ proto3 string/bytes fields ║
65 // ║ null │ proto2 scalar fields ║
66 // ║ null │ message fields ║
67 // ║ [] │ list fields ║
68 // ║ {} │ map fields ║
69 // ╚═══════╧════════════════════════════╝
Herbie Ong984e5282019-09-06 00:29:48 -070070 EmitUnpopulated bool
71
Joe Tsai74b14602020-01-06 15:44:09 -080072 // Multiline specifies whether the marshaler should format the output in
73 // indented-form with every textual element on a new line.
74 // If Indent is an empty string, then an arbitrary indent is chosen.
75 Multiline bool
76
77 // Indent specifies the set of indentation characters to use in a multiline
78 // formatted output such that every entry is preceded by Indent and
79 // terminated by a newline. If non-empty, then Multiline is treated as true.
80 // Indent can only be composed of space or tab characters.
Herbie Ong0b0f4032019-03-18 19:06:15 -070081 Indent string
82
Joe Tsai1c283042019-05-14 14:28:19 -070083 // Resolver is used for looking up types when expanding google.protobuf.Any
84 // messages. If nil, this defaults to using protoregistry.GlobalTypes.
85 Resolver interface {
Damien Neil95faac22019-06-19 10:03:37 -070086 protoregistry.ExtensionTypeResolver
Joe Tsai1c283042019-05-14 14:28:19 -070087 protoregistry.MessageTypeResolver
88 }
Herbie Ong7b828bc2019-02-08 19:56:24 -080089}
90
Joe Tsai74b14602020-01-06 15:44:09 -080091// Format formats the message as a string.
92// This method is only intended for human consumption and ignores errors.
93// Do not depend on the output being stable. It may change over time across
94// different versions of the program.
95func (o MarshalOptions) Format(m proto.Message) string {
96 if m == nil || !m.ProtoReflect().IsValid() {
97 return "<nil>" // invalid syntax, but okay since this is for debugging
98 }
99 o.AllowPartial = true
100 b, _ := o.Marshal(m)
101 return string(b)
102}
103
Herbie Ong0b0f4032019-03-18 19:06:15 -0700104// Marshal marshals the given proto.Message in the JSON format using options in
Herbie Ong582ab3d2019-09-06 15:56:09 -0700105// MarshalOptions. Do not depend on the output being stable. It may change over
106// time across different versions of the program.
Herbie Ong7b828bc2019-02-08 19:56:24 -0800107func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
Joe Tsai74b14602020-01-06 15:44:09 -0800108 if o.Multiline && o.Indent == "" {
109 o.Indent = defaultIndent
110 }
111 if o.Resolver == nil {
112 o.Resolver = protoregistry.GlobalTypes
113 }
114
Herbie Ong822de2d2019-03-27 13:16:23 -0700115 var err error
116 o.encoder, err = json.NewEncoder(o.Indent)
Herbie Ong87608a72019-03-06 14:32:24 -0800117 if err != nil {
118 return nil, err
119 }
120
Herbie Ong822de2d2019-03-27 13:16:23 -0700121 err = o.marshalMessage(m.ProtoReflect())
Damien Neil8c86fc52019-06-19 09:28:29 -0700122 if err != nil {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800123 return nil, err
124 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700125 if o.AllowPartial {
126 return o.encoder.Bytes(), nil
Damien Neil4686e232019-04-05 13:31:40 -0700127 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700128 return o.encoder.Bytes(), proto.IsInitialized(m)
Herbie Ong87608a72019-03-06 14:32:24 -0800129}
130
131// marshalMessage marshals the given protoreflect.Message.
Herbie Ong822de2d2019-03-27 13:16:23 -0700132func (o MarshalOptions) marshalMessage(m pref.Message) error {
Joe Tsai0fc49f82019-05-01 12:29:25 -0700133 if isCustomType(m.Descriptor().FullName()) {
Herbie Ong822de2d2019-03-27 13:16:23 -0700134 return o.marshalCustomType(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -0700135 }
136
Herbie Ong822de2d2019-03-27 13:16:23 -0700137 o.encoder.StartObject()
138 defer o.encoder.EndObject()
Damien Neil8c86fc52019-06-19 09:28:29 -0700139 if err := o.marshalFields(m); err != nil {
Herbie Ong0b0f4032019-03-18 19:06:15 -0700140 return err
141 }
Herbie Ong87608a72019-03-06 14:32:24 -0800142
Damien Neil8c86fc52019-06-19 09:28:29 -0700143 return nil
Herbie Ong0b0f4032019-03-18 19:06:15 -0700144}
145
146// marshalFields marshals the fields in the given protoreflect.Message.
Herbie Ong822de2d2019-03-27 13:16:23 -0700147func (o MarshalOptions) marshalFields(m pref.Message) error {
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700148 messageDesc := m.Descriptor()
Joe Tsai1799d112019-08-08 13:31:59 -0700149 if !flags.ProtoLegacy && messageset.IsMessageSet(messageDesc) {
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700150 return errors.New("no support for proto1 MessageSets")
151 }
152
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700153 // Marshal out known fields.
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700154 fieldDescs := messageDesc.Fields()
Joe Tsaifc5f8c32019-09-17 22:32:53 -0700155 for i := 0; i < fieldDescs.Len(); {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800156 fd := fieldDescs.Get(i)
Joe Tsaifc5f8c32019-09-17 22:32:53 -0700157 if od := fd.ContainingOneof(); od != nil {
158 fd = m.WhichOneof(od)
159 i += od.Fields().Len()
160 if fd == nil {
161 continue // unpopulated oneofs are not affected by EmitUnpopulated
162 }
163 } else {
164 i++
165 }
166
Herbie Ong984e5282019-09-06 00:29:48 -0700167 val := m.Get(fd)
Joe Tsai378c1322019-04-25 23:48:08 -0700168 if !m.Has(fd) {
Joe Tsaifc5f8c32019-09-17 22:32:53 -0700169 if !o.EmitUnpopulated {
Herbie Ong984e5282019-09-06 00:29:48 -0700170 continue
171 }
172 isProto2Scalar := fd.Syntax() == pref.Proto2 && fd.Default().IsValid()
173 isSingularMessage := fd.Cardinality() != pref.Repeated && fd.Message() != nil
174 if isProto2Scalar || isSingularMessage {
175 // Use invalid value to emit null.
176 val = pref.Value{}
177 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800178 }
179
Herbie Ong87608a72019-03-06 14:32:24 -0800180 name := fd.JSONName()
Herbie Ong956cd6d2019-09-06 15:17:22 -0700181 if o.UseProtoNames {
182 name = string(fd.Name())
183 // Use type name for group field name.
184 if fd.Kind() == pref.GroupKind {
185 name = string(fd.Message().Name())
186 }
187 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700188 if err := o.encoder.WriteName(name); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800189 return err
190 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700191 if err := o.marshalValue(val, fd); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800192 return err
Herbie Ong7b828bc2019-02-08 19:56:24 -0800193 }
194 }
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700195
196 // Marshal out extensions.
Damien Neil8c86fc52019-06-19 09:28:29 -0700197 if err := o.marshalExtensions(m); err != nil {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700198 return err
199 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700200 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800201}
202
Herbie Ong87608a72019-03-06 14:32:24 -0800203// marshalValue marshals the given protoreflect.Value.
Herbie Ong822de2d2019-03-27 13:16:23 -0700204func (o MarshalOptions) marshalValue(val pref.Value, fd pref.FieldDescriptor) error {
Joe Tsaiac31a352019-05-13 14:32:56 -0700205 switch {
206 case fd.IsList():
207 return o.marshalList(val.List(), fd)
208 case fd.IsMap():
209 return o.marshalMap(val.Map(), fd)
210 default:
211 return o.marshalSingular(val, fd)
Herbie Ong7b828bc2019-02-08 19:56:24 -0800212 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800213}
214
Herbie Ong87608a72019-03-06 14:32:24 -0800215// marshalSingular marshals the given non-repeated field value. This includes
216// all scalar types, enums, messages, and groups.
Herbie Ong822de2d2019-03-27 13:16:23 -0700217func (o MarshalOptions) marshalSingular(val pref.Value, fd pref.FieldDescriptor) error {
Herbie Ong984e5282019-09-06 00:29:48 -0700218 if !val.IsValid() {
219 o.encoder.WriteNull()
220 return nil
221 }
222
Herbie Ong87608a72019-03-06 14:32:24 -0800223 switch kind := fd.Kind(); kind {
224 case pref.BoolKind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700225 o.encoder.WriteBool(val.Bool())
Herbie Ong87608a72019-03-06 14:32:24 -0800226
227 case pref.StringKind:
Damien Neil8c86fc52019-06-19 09:28:29 -0700228 if err := o.encoder.WriteString(val.String()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800229 return err
230 }
231
232 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700233 o.encoder.WriteInt(val.Int())
Herbie Ong87608a72019-03-06 14:32:24 -0800234
235 case pref.Uint32Kind, pref.Fixed32Kind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700236 o.encoder.WriteUint(val.Uint())
Herbie Ong7b828bc2019-02-08 19:56:24 -0800237
238 case pref.Int64Kind, pref.Sint64Kind, pref.Uint64Kind,
239 pref.Sfixed64Kind, pref.Fixed64Kind:
Herbie Ong87608a72019-03-06 14:32:24 -0800240 // 64-bit integers are written out as JSON string.
Herbie Ong822de2d2019-03-27 13:16:23 -0700241 o.encoder.WriteString(val.String())
Herbie Ong7b828bc2019-02-08 19:56:24 -0800242
Herbie Ong87608a72019-03-06 14:32:24 -0800243 case pref.FloatKind:
244 // Encoder.WriteFloat handles the special numbers NaN and infinites.
Herbie Ong822de2d2019-03-27 13:16:23 -0700245 o.encoder.WriteFloat(val.Float(), 32)
Herbie Ong87608a72019-03-06 14:32:24 -0800246
247 case pref.DoubleKind:
248 // Encoder.WriteFloat handles the special numbers NaN and infinites.
Herbie Ong822de2d2019-03-27 13:16:23 -0700249 o.encoder.WriteFloat(val.Float(), 64)
Herbie Ong7b828bc2019-02-08 19:56:24 -0800250
251 case pref.BytesKind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700252 err := o.encoder.WriteString(base64.StdEncoding.EncodeToString(val.Bytes()))
Damien Neil8c86fc52019-06-19 09:28:29 -0700253 if err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800254 return err
255 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800256
257 case pref.EnumKind:
Joe Tsaid24bc722019-04-15 23:39:09 -0700258 if fd.Enum().FullName() == "google.protobuf.NullValue" {
Herbie Ong822de2d2019-03-27 13:16:23 -0700259 o.encoder.WriteNull()
Herbie Ong87608a72019-03-06 14:32:24 -0800260 } else {
Herbie Ong9111f3b2019-09-06 14:35:09 -0700261 desc := fd.Enum().Values().ByNumber(val.Enum())
262 if o.UseEnumNumbers || desc == nil {
263 o.encoder.WriteInt(int64(val.Enum()))
264 } else {
265 err := o.encoder.WriteString(string(desc.Name()))
266 if err != nil {
267 return err
268 }
269 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800270 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800271
272 case pref.MessageKind, pref.GroupKind:
Damien Neil8c86fc52019-06-19 09:28:29 -0700273 if err := o.marshalMessage(val.Message()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800274 return err
275 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800276
Herbie Ong87608a72019-03-06 14:32:24 -0800277 default:
Herbie Ong0b0f4032019-03-18 19:06:15 -0700278 panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind))
Herbie Ong87608a72019-03-06 14:32:24 -0800279 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700280 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800281}
282
Herbie Ong87608a72019-03-06 14:32:24 -0800283// marshalList marshals the given protoreflect.List.
Herbie Ong822de2d2019-03-27 13:16:23 -0700284func (o MarshalOptions) marshalList(list pref.List, fd pref.FieldDescriptor) error {
285 o.encoder.StartArray()
286 defer o.encoder.EndArray()
Herbie Ong87608a72019-03-06 14:32:24 -0800287
Herbie Ong87608a72019-03-06 14:32:24 -0800288 for i := 0; i < list.Len(); i++ {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800289 item := list.Get(i)
Damien Neil8c86fc52019-06-19 09:28:29 -0700290 if err := o.marshalSingular(item, fd); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800291 return err
Herbie Ong7b828bc2019-02-08 19:56:24 -0800292 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800293 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700294 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800295}
296
297type mapEntry struct {
298 key pref.MapKey
299 value pref.Value
300}
301
Herbie Ong87608a72019-03-06 14:32:24 -0800302// marshalMap marshals given protoreflect.Map.
Herbie Ong822de2d2019-03-27 13:16:23 -0700303func (o MarshalOptions) marshalMap(mmap pref.Map, fd pref.FieldDescriptor) error {
304 o.encoder.StartObject()
305 defer o.encoder.EndObject()
Herbie Ong87608a72019-03-06 14:32:24 -0800306
Herbie Ong7b828bc2019-02-08 19:56:24 -0800307 // Get a sorted list based on keyType first.
308 entries := make([]mapEntry, 0, mmap.Len())
309 mmap.Range(func(key pref.MapKey, val pref.Value) bool {
310 entries = append(entries, mapEntry{key: key, value: val})
311 return true
312 })
Joe Tsaiac31a352019-05-13 14:32:56 -0700313 sortMap(fd.MapKey().Kind(), entries)
Herbie Ong7b828bc2019-02-08 19:56:24 -0800314
Herbie Ong87608a72019-03-06 14:32:24 -0800315 // Write out sorted list.
Herbie Ong7b828bc2019-02-08 19:56:24 -0800316 for _, entry := range entries {
Damien Neil8c86fc52019-06-19 09:28:29 -0700317 if err := o.encoder.WriteName(entry.key.String()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800318 return err
Herbie Ong7b828bc2019-02-08 19:56:24 -0800319 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700320 if err := o.marshalSingular(entry.value, fd.MapValue()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800321 return err
322 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800323 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700324 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800325}
326
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700327// sortMap orders list based on value of key field for deterministic ordering.
Herbie Ong7b828bc2019-02-08 19:56:24 -0800328func sortMap(keyKind pref.Kind, values []mapEntry) {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700329 sort.Slice(values, func(i, j int) bool {
330 switch keyKind {
331 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind,
332 pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
Herbie Ong7b828bc2019-02-08 19:56:24 -0800333 return values[i].key.Int() < values[j].key.Int()
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700334
335 case pref.Uint32Kind, pref.Fixed32Kind,
336 pref.Uint64Kind, pref.Fixed64Kind:
Herbie Ong7b828bc2019-02-08 19:56:24 -0800337 return values[i].key.Uint() < values[j].key.Uint()
338 }
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700339 return values[i].key.String() < values[j].key.String()
340 })
341}
342
343// marshalExtensions marshals extension fields.
Joe Tsai378c1322019-04-25 23:48:08 -0700344func (o MarshalOptions) marshalExtensions(m pref.Message) error {
345 type entry struct {
346 key string
347 value pref.Value
348 desc pref.FieldDescriptor
Herbie Ong7b828bc2019-02-08 19:56:24 -0800349 }
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700350
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700351 // Get a sorted list based on field key first.
Joe Tsai378c1322019-04-25 23:48:08 -0700352 var entries []entry
353 m.Range(func(fd pref.FieldDescriptor, v pref.Value) bool {
354 if !fd.IsExtension() {
355 return true
356 }
Joe Tsai378c1322019-04-25 23:48:08 -0700357
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700358 // For MessageSet extensions, the name used is the parent message.
Joe Tsaid4211502019-07-02 14:58:02 -0700359 name := fd.FullName()
Joe Tsai5ae10aa2019-07-11 18:23:08 -0700360 if messageset.IsMessageSetExtension(fd) {
361 name = name.Parent()
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700362 }
363
Joe Tsai378c1322019-04-25 23:48:08 -0700364 // Use [name] format for JSON field name.
365 entries = append(entries, entry{
366 key: string(name),
367 value: v,
368 desc: fd,
369 })
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700370 return true
371 })
372
373 // Sort extensions lexicographically.
374 sort.Slice(entries, func(i, j int) bool {
375 return entries[i].key < entries[j].key
376 })
377
378 // Write out sorted list.
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700379 for _, entry := range entries {
380 // JSON field name is the proto field name enclosed in [], similar to
381 // textproto. This is consistent with Go v1 lib. C++ lib v3.7.0 does not
382 // marshal out extension fields.
Damien Neil8c86fc52019-06-19 09:28:29 -0700383 if err := o.encoder.WriteName("[" + entry.key + "]"); err != nil {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700384 return err
385 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700386 if err := o.marshalValue(entry.value, entry.desc); err != nil {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700387 return err
388 }
389 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700390 return nil
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700391}