blob: f17d3bb635478c6b9b1a15007971615a4869d24b [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"
Damien Neile89e6242019-05-13 23:55:40 -070013 "google.golang.org/protobuf/internal/pragma"
14 "google.golang.org/protobuf/proto"
15 pref "google.golang.org/protobuf/reflect/protoreflect"
16 "google.golang.org/protobuf/reflect/protoregistry"
Herbie Ong7b828bc2019-02-08 19:56:24 -080017)
18
19// Marshal writes the given proto.Message in JSON format using default options.
20func Marshal(m proto.Message) ([]byte, error) {
21 return MarshalOptions{}.Marshal(m)
22}
23
24// MarshalOptions is a configurable JSON format marshaler.
25type MarshalOptions struct {
26 pragma.NoUnkeyedLiterals
27
Herbie Ong329be5b2019-03-27 14:47:59 -070028 // AllowPartial allows messages that have missing required fields to marshal
29 // without returning an error. If AllowPartial is false (the default),
30 // Marshal will return error if there are any missing required fields.
31 AllowPartial bool
32
Herbie Ong0b0f4032019-03-18 19:06:15 -070033 // If Indent is a non-empty string, it causes entries for an Array or Object
34 // to be preceded by the indent and trailed by a newline. Indent can only be
35 // composed of space or tab characters.
36 Indent string
37
Joe Tsai1c283042019-05-14 14:28:19 -070038 // Resolver is used for looking up types when expanding google.protobuf.Any
39 // messages. If nil, this defaults to using protoregistry.GlobalTypes.
40 Resolver interface {
Damien Neil95faac22019-06-19 10:03:37 -070041 protoregistry.ExtensionTypeResolver
Joe Tsai1c283042019-05-14 14:28:19 -070042 protoregistry.MessageTypeResolver
43 }
Herbie Ong822de2d2019-03-27 13:16:23 -070044
45 encoder *json.Encoder
Herbie Ong7b828bc2019-02-08 19:56:24 -080046}
47
Herbie Ong0b0f4032019-03-18 19:06:15 -070048// Marshal marshals the given proto.Message in the JSON format using options in
49// MarshalOptions.
Herbie Ong7b828bc2019-02-08 19:56:24 -080050func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
Herbie Ong822de2d2019-03-27 13:16:23 -070051 var err error
52 o.encoder, err = json.NewEncoder(o.Indent)
Herbie Ong87608a72019-03-06 14:32:24 -080053 if err != nil {
54 return nil, err
55 }
Herbie Ong822de2d2019-03-27 13:16:23 -070056 if o.Resolver == nil {
57 o.Resolver = protoregistry.GlobalTypes
58 }
Herbie Ong87608a72019-03-06 14:32:24 -080059
Herbie Ong822de2d2019-03-27 13:16:23 -070060 err = o.marshalMessage(m.ProtoReflect())
Damien Neil8c86fc52019-06-19 09:28:29 -070061 if err != nil {
Herbie Ong7b828bc2019-02-08 19:56:24 -080062 return nil, err
63 }
Damien Neil8c86fc52019-06-19 09:28:29 -070064 if o.AllowPartial {
65 return o.encoder.Bytes(), nil
Damien Neil4686e232019-04-05 13:31:40 -070066 }
Damien Neil8c86fc52019-06-19 09:28:29 -070067 return o.encoder.Bytes(), proto.IsInitialized(m)
Herbie Ong87608a72019-03-06 14:32:24 -080068}
69
70// marshalMessage marshals the given protoreflect.Message.
Herbie Ong822de2d2019-03-27 13:16:23 -070071func (o MarshalOptions) marshalMessage(m pref.Message) error {
Joe Tsai0fc49f82019-05-01 12:29:25 -070072 if isCustomType(m.Descriptor().FullName()) {
Herbie Ong822de2d2019-03-27 13:16:23 -070073 return o.marshalCustomType(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -070074 }
75
Herbie Ong822de2d2019-03-27 13:16:23 -070076 o.encoder.StartObject()
77 defer o.encoder.EndObject()
Damien Neil8c86fc52019-06-19 09:28:29 -070078 if err := o.marshalFields(m); err != nil {
Herbie Ong0b0f4032019-03-18 19:06:15 -070079 return err
80 }
Herbie Ong87608a72019-03-06 14:32:24 -080081
Damien Neil8c86fc52019-06-19 09:28:29 -070082 return nil
Herbie Ong0b0f4032019-03-18 19:06:15 -070083}
84
85// marshalFields marshals the fields in the given protoreflect.Message.
Herbie Ong822de2d2019-03-27 13:16:23 -070086func (o MarshalOptions) marshalFields(m pref.Message) error {
Herbie Ongf83d5bb2019-03-14 00:01:27 -070087 // Marshal out known fields.
Joe Tsai378c1322019-04-25 23:48:08 -070088 fieldDescs := m.Descriptor().Fields()
Herbie Ong87608a72019-03-06 14:32:24 -080089 for i := 0; i < fieldDescs.Len(); i++ {
Herbie Ong7b828bc2019-02-08 19:56:24 -080090 fd := fieldDescs.Get(i)
Joe Tsai378c1322019-04-25 23:48:08 -070091 if !m.Has(fd) {
Herbie Ong7b828bc2019-02-08 19:56:24 -080092 continue
93 }
94
Herbie Ong87608a72019-03-06 14:32:24 -080095 name := fd.JSONName()
Joe Tsai378c1322019-04-25 23:48:08 -070096 val := m.Get(fd)
Damien Neil8c86fc52019-06-19 09:28:29 -070097 if err := o.encoder.WriteName(name); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -080098 return err
99 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700100 if err := o.marshalValue(val, fd); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800101 return err
Herbie Ong7b828bc2019-02-08 19:56:24 -0800102 }
103 }
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700104
105 // Marshal out extensions.
Damien Neil8c86fc52019-06-19 09:28:29 -0700106 if err := o.marshalExtensions(m); err != nil {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700107 return err
108 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700109 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800110}
111
Herbie Ong87608a72019-03-06 14:32:24 -0800112// marshalValue marshals the given protoreflect.Value.
Herbie Ong822de2d2019-03-27 13:16:23 -0700113func (o MarshalOptions) marshalValue(val pref.Value, fd pref.FieldDescriptor) error {
Joe Tsaiac31a352019-05-13 14:32:56 -0700114 switch {
115 case fd.IsList():
116 return o.marshalList(val.List(), fd)
117 case fd.IsMap():
118 return o.marshalMap(val.Map(), fd)
119 default:
120 return o.marshalSingular(val, fd)
Herbie Ong7b828bc2019-02-08 19:56:24 -0800121 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800122}
123
Herbie Ong87608a72019-03-06 14:32:24 -0800124// marshalSingular marshals the given non-repeated field value. This includes
125// all scalar types, enums, messages, and groups.
Herbie Ong822de2d2019-03-27 13:16:23 -0700126func (o MarshalOptions) marshalSingular(val pref.Value, fd pref.FieldDescriptor) error {
Herbie Ong87608a72019-03-06 14:32:24 -0800127 switch kind := fd.Kind(); kind {
128 case pref.BoolKind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700129 o.encoder.WriteBool(val.Bool())
Herbie Ong87608a72019-03-06 14:32:24 -0800130
131 case pref.StringKind:
Damien Neil8c86fc52019-06-19 09:28:29 -0700132 if err := o.encoder.WriteString(val.String()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800133 return err
134 }
135
136 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700137 o.encoder.WriteInt(val.Int())
Herbie Ong87608a72019-03-06 14:32:24 -0800138
139 case pref.Uint32Kind, pref.Fixed32Kind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700140 o.encoder.WriteUint(val.Uint())
Herbie Ong7b828bc2019-02-08 19:56:24 -0800141
142 case pref.Int64Kind, pref.Sint64Kind, pref.Uint64Kind,
143 pref.Sfixed64Kind, pref.Fixed64Kind:
Herbie Ong87608a72019-03-06 14:32:24 -0800144 // 64-bit integers are written out as JSON string.
Herbie Ong822de2d2019-03-27 13:16:23 -0700145 o.encoder.WriteString(val.String())
Herbie Ong7b828bc2019-02-08 19:56:24 -0800146
Herbie Ong87608a72019-03-06 14:32:24 -0800147 case pref.FloatKind:
148 // Encoder.WriteFloat handles the special numbers NaN and infinites.
Herbie Ong822de2d2019-03-27 13:16:23 -0700149 o.encoder.WriteFloat(val.Float(), 32)
Herbie Ong87608a72019-03-06 14:32:24 -0800150
151 case pref.DoubleKind:
152 // Encoder.WriteFloat handles the special numbers NaN and infinites.
Herbie Ong822de2d2019-03-27 13:16:23 -0700153 o.encoder.WriteFloat(val.Float(), 64)
Herbie Ong7b828bc2019-02-08 19:56:24 -0800154
155 case pref.BytesKind:
Herbie Ong822de2d2019-03-27 13:16:23 -0700156 err := o.encoder.WriteString(base64.StdEncoding.EncodeToString(val.Bytes()))
Damien Neil8c86fc52019-06-19 09:28:29 -0700157 if err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800158 return err
159 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800160
161 case pref.EnumKind:
Joe Tsaid24bc722019-04-15 23:39:09 -0700162 if fd.Enum().FullName() == "google.protobuf.NullValue" {
Herbie Ong822de2d2019-03-27 13:16:23 -0700163 o.encoder.WriteNull()
Joe Tsaid24bc722019-04-15 23:39:09 -0700164 } else if desc := fd.Enum().Values().ByNumber(val.Enum()); desc != nil {
Herbie Ong822de2d2019-03-27 13:16:23 -0700165 err := o.encoder.WriteString(string(desc.Name()))
Damien Neil8c86fc52019-06-19 09:28:29 -0700166 if err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800167 return err
168 }
169 } else {
170 // Use numeric value if there is no enum value descriptor.
Joe Tsaid24bc722019-04-15 23:39:09 -0700171 o.encoder.WriteInt(int64(val.Enum()))
Herbie Ong7b828bc2019-02-08 19:56:24 -0800172 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800173
174 case pref.MessageKind, pref.GroupKind:
Damien Neil8c86fc52019-06-19 09:28:29 -0700175 if err := o.marshalMessage(val.Message()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800176 return err
177 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800178
Herbie Ong87608a72019-03-06 14:32:24 -0800179 default:
Herbie Ong0b0f4032019-03-18 19:06:15 -0700180 panic(fmt.Sprintf("%v has unknown kind: %v", fd.FullName(), kind))
Herbie Ong87608a72019-03-06 14:32:24 -0800181 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700182 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800183}
184
Herbie Ong87608a72019-03-06 14:32:24 -0800185// marshalList marshals the given protoreflect.List.
Herbie Ong822de2d2019-03-27 13:16:23 -0700186func (o MarshalOptions) marshalList(list pref.List, fd pref.FieldDescriptor) error {
187 o.encoder.StartArray()
188 defer o.encoder.EndArray()
Herbie Ong87608a72019-03-06 14:32:24 -0800189
Herbie Ong87608a72019-03-06 14:32:24 -0800190 for i := 0; i < list.Len(); i++ {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800191 item := list.Get(i)
Damien Neil8c86fc52019-06-19 09:28:29 -0700192 if err := o.marshalSingular(item, fd); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800193 return err
Herbie Ong7b828bc2019-02-08 19:56:24 -0800194 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800195 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700196 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800197}
198
199type mapEntry struct {
200 key pref.MapKey
201 value pref.Value
202}
203
Herbie Ong87608a72019-03-06 14:32:24 -0800204// marshalMap marshals given protoreflect.Map.
Herbie Ong822de2d2019-03-27 13:16:23 -0700205func (o MarshalOptions) marshalMap(mmap pref.Map, fd pref.FieldDescriptor) error {
206 o.encoder.StartObject()
207 defer o.encoder.EndObject()
Herbie Ong87608a72019-03-06 14:32:24 -0800208
Herbie Ong7b828bc2019-02-08 19:56:24 -0800209 // Get a sorted list based on keyType first.
210 entries := make([]mapEntry, 0, mmap.Len())
211 mmap.Range(func(key pref.MapKey, val pref.Value) bool {
212 entries = append(entries, mapEntry{key: key, value: val})
213 return true
214 })
Joe Tsaiac31a352019-05-13 14:32:56 -0700215 sortMap(fd.MapKey().Kind(), entries)
Herbie Ong7b828bc2019-02-08 19:56:24 -0800216
Herbie Ong87608a72019-03-06 14:32:24 -0800217 // Write out sorted list.
Herbie Ong7b828bc2019-02-08 19:56:24 -0800218 for _, entry := range entries {
Damien Neil8c86fc52019-06-19 09:28:29 -0700219 if err := o.encoder.WriteName(entry.key.String()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800220 return err
Herbie Ong7b828bc2019-02-08 19:56:24 -0800221 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700222 if err := o.marshalSingular(entry.value, fd.MapValue()); err != nil {
Herbie Ong87608a72019-03-06 14:32:24 -0800223 return err
224 }
Herbie Ong7b828bc2019-02-08 19:56:24 -0800225 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700226 return nil
Herbie Ong7b828bc2019-02-08 19:56:24 -0800227}
228
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700229// sortMap orders list based on value of key field for deterministic ordering.
Herbie Ong7b828bc2019-02-08 19:56:24 -0800230func sortMap(keyKind pref.Kind, values []mapEntry) {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700231 sort.Slice(values, func(i, j int) bool {
232 switch keyKind {
233 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind,
234 pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
Herbie Ong7b828bc2019-02-08 19:56:24 -0800235 return values[i].key.Int() < values[j].key.Int()
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700236
237 case pref.Uint32Kind, pref.Fixed32Kind,
238 pref.Uint64Kind, pref.Fixed64Kind:
Herbie Ong7b828bc2019-02-08 19:56:24 -0800239 return values[i].key.Uint() < values[j].key.Uint()
240 }
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700241 return values[i].key.String() < values[j].key.String()
242 })
243}
244
245// marshalExtensions marshals extension fields.
Joe Tsai378c1322019-04-25 23:48:08 -0700246func (o MarshalOptions) marshalExtensions(m pref.Message) error {
247 type entry struct {
248 key string
249 value pref.Value
250 desc pref.FieldDescriptor
Herbie Ong7b828bc2019-02-08 19:56:24 -0800251 }
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700252
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700253 // Get a sorted list based on field key first.
Joe Tsai378c1322019-04-25 23:48:08 -0700254 var entries []entry
255 m.Range(func(fd pref.FieldDescriptor, v pref.Value) bool {
256 if !fd.IsExtension() {
257 return true
258 }
259 xt := fd.(pref.ExtensionType)
260
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700261 // If extended type is a MessageSet, set field name to be the message type name.
Joe Tsai378c1322019-04-25 23:48:08 -0700262 name := xt.Descriptor().FullName()
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700263 if isMessageSetExtension(xt) {
Joe Tsai0fc49f82019-05-01 12:29:25 -0700264 name = xt.Descriptor().Message().FullName()
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700265 }
266
Joe Tsai378c1322019-04-25 23:48:08 -0700267 // Use [name] format for JSON field name.
268 entries = append(entries, entry{
269 key: string(name),
270 value: v,
271 desc: fd,
272 })
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700273 return true
274 })
275
276 // Sort extensions lexicographically.
277 sort.Slice(entries, func(i, j int) bool {
278 return entries[i].key < entries[j].key
279 })
280
281 // Write out sorted list.
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700282 for _, entry := range entries {
283 // JSON field name is the proto field name enclosed in [], similar to
284 // textproto. This is consistent with Go v1 lib. C++ lib v3.7.0 does not
285 // marshal out extension fields.
Damien Neil8c86fc52019-06-19 09:28:29 -0700286 if err := o.encoder.WriteName("[" + entry.key + "]"); err != nil {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700287 return err
288 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700289 if err := o.marshalValue(entry.value, entry.desc); err != nil {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700290 return err
291 }
292 }
Damien Neil8c86fc52019-06-19 09:28:29 -0700293 return nil
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700294}
295
296// isMessageSetExtension reports whether extension extends a message set.
297func isMessageSetExtension(xt pref.ExtensionType) bool {
Joe Tsai0fc49f82019-05-01 12:29:25 -0700298 xd := xt.Descriptor()
299 if xd.Name() != "message_set_extension" {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700300 return false
301 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700302 md := xd.Message()
Joe Tsaid24bc722019-04-15 23:39:09 -0700303 if md == nil {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700304 return false
305 }
Joe Tsai0fc49f82019-05-01 12:29:25 -0700306 if xd.FullName().Parent() != md.FullName() {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700307 return false
308 }
Joe Tsaiac31a352019-05-13 14:32:56 -0700309 xmd, ok := xd.ContainingMessage().(interface{ IsMessageSet() bool })
Joe Tsaid24bc722019-04-15 23:39:09 -0700310 return ok && xmd.IsMessageSet()
Herbie Ong7b828bc2019-02-08 19:56:24 -0800311}