blob: 928b24fa23df31c832abc9b7530f734408d6a470 [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
5package jsonpb
6
7import (
8 "encoding/base64"
9 "math"
10 "sort"
11
12 "github.com/golang/protobuf/v2/internal/encoding/json"
13 "github.com/golang/protobuf/v2/internal/errors"
14 "github.com/golang/protobuf/v2/internal/pragma"
15 "github.com/golang/protobuf/v2/proto"
16 pref "github.com/golang/protobuf/v2/reflect/protoreflect"
17)
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
28 // Set Compact to true to have output in a single line with no line breaks.
29 Compact bool
30}
31
32// Marshal writes the given proto.Message in JSON format using options in MarshalOptions object.
33func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
34 var nerr errors.NonFatal
35 v, err := o.marshalMessage(m.ProtoReflect())
36 if !nerr.Merge(err) {
37 return nil, err
38 }
39
40 indent := " "
41 if o.Compact {
42 indent = ""
43 }
44
45 b, err := json.Marshal(v, indent)
46 if !nerr.Merge(err) {
47 return nil, err
48 }
49 return b, nerr.E
50}
51
52// marshalMessage converts a protoreflect.Message to a json.Value.
53func (o MarshalOptions) marshalMessage(m pref.Message) (json.Value, error) {
54 var nerr errors.NonFatal
55 var msgFields [][2]json.Value
56
57 msgType := m.Type()
58 fieldDescs := msgType.Fields()
59 knownFields := m.KnownFields()
60 size := fieldDescs.Len()
61 for i := 0; i < size; i++ {
62 fd := fieldDescs.Get(i)
63 num := fd.Number()
64
65 if !knownFields.Has(num) {
66 if fd.Cardinality() == pref.Required {
67 // Treat unset required fields as a non-fatal error.
68 nerr.AppendRequiredNotSet(string(fd.FullName()))
69 }
70 continue
71 }
72
73 name := json.ValueOf(fd.JSONName())
74 pval := knownFields.Get(num)
75 var err error
76 msgFields, err = o.appendField(msgFields, name, pval, fd)
77 if !nerr.Merge(err) {
78 return json.Value{}, err
79 }
80 }
81
82 return json.ValueOf(msgFields), nerr.E
83}
84
85// appendField marshals a protoreflect.Value and appends it to the given
86// [][2]json.Value.
87func (o MarshalOptions) appendField(msgFields [][2]json.Value, name json.Value, pval pref.Value, fd pref.FieldDescriptor) ([][2]json.Value, error) {
88 var nerr errors.NonFatal
89 var jval json.Value
90 var err error
91
92 if fd.Cardinality() == pref.Repeated {
93 // Map or repeated fields.
94 if fd.IsMap() {
95 jval, err = o.marshalMap(pval.Map(), fd)
96 if !nerr.Merge(err) {
97 return msgFields, err
98 }
99 } else {
100 jval, err = o.marshalList(pval.List(), fd)
101 if !nerr.Merge(err) {
102 return msgFields, err
103 }
104 }
105 } else {
106 // Required or optional fields.
107 jval, err = o.marshalSingular(pval, fd)
108 if !nerr.Merge(err) {
109 return msgFields, err
110 }
111 }
112
113 msgFields = append(msgFields, [2]json.Value{name, jval})
114 return msgFields, nerr.E
115}
116
117// marshalSingular converts a non-repeated field value to json.Value.
118// This includes all scalar types, enums, messages, and groups.
119func (o MarshalOptions) marshalSingular(val pref.Value, fd pref.FieldDescriptor) (json.Value, error) {
120 kind := fd.Kind()
121 switch kind {
122 case pref.BoolKind, pref.StringKind,
123 pref.Int32Kind, pref.Sint32Kind, pref.Uint32Kind,
124 pref.Sfixed32Kind, pref.Fixed32Kind:
125 return json.ValueOf(val.Interface()), nil
126
127 case pref.Int64Kind, pref.Sint64Kind, pref.Uint64Kind,
128 pref.Sfixed64Kind, pref.Fixed64Kind:
129 return json.ValueOf(val.String()), nil
130
131 case pref.FloatKind, pref.DoubleKind:
132 n := val.Float()
133 switch {
134 case math.IsNaN(n):
135 return json.ValueOf("NaN"), nil
136 case math.IsInf(n, +1):
137 return json.ValueOf("Infinity"), nil
138 case math.IsInf(n, -1):
139 return json.ValueOf("-Infinity"), nil
140 default:
141 return json.ValueOf(n), nil
142 }
143
144 case pref.BytesKind:
145 return json.ValueOf(base64.StdEncoding.EncodeToString(val.Bytes())), nil
146
147 case pref.EnumKind:
148 num := val.Enum()
149 if desc := fd.EnumType().Values().ByNumber(num); desc != nil {
150 return json.ValueOf(string(desc.Name())), nil
151 }
152 // Use numeric value if there is no enum value descriptor.
153 return json.ValueOf(int32(num)), nil
154
155 case pref.MessageKind, pref.GroupKind:
156 return o.marshalMessage(val.Message())
157 }
158
159 return json.Value{}, errors.New("%v has unknown kind: %v", fd.FullName(), kind)
160}
161
162// marshalList converts a protoreflect.List to json.Value.
163func (o MarshalOptions) marshalList(list pref.List, fd pref.FieldDescriptor) (json.Value, error) {
164 var nerr errors.NonFatal
165 size := list.Len()
166 values := make([]json.Value, 0, size)
167
168 for i := 0; i < size; i++ {
169 item := list.Get(i)
170 val, err := o.marshalSingular(item, fd)
171 if !nerr.Merge(err) {
172 return json.Value{}, err
173 }
174 values = append(values, val)
175 }
176
177 return json.ValueOf(values), nerr.E
178}
179
180type mapEntry struct {
181 key pref.MapKey
182 value pref.Value
183}
184
185// marshalMap converts a protoreflect.Map to json.Value.
186func (o MarshalOptions) marshalMap(mmap pref.Map, fd pref.FieldDescriptor) (json.Value, error) {
187 msgFields := fd.MessageType().Fields()
188 keyType := msgFields.ByNumber(1)
189 valType := msgFields.ByNumber(2)
190
191 // Get a sorted list based on keyType first.
192 entries := make([]mapEntry, 0, mmap.Len())
193 mmap.Range(func(key pref.MapKey, val pref.Value) bool {
194 entries = append(entries, mapEntry{key: key, value: val})
195 return true
196 })
197 sortMap(keyType.Kind(), entries)
198
199 // Convert to list of [2]json.Value.
200 var nerr errors.NonFatal
201 values := make([][2]json.Value, 0, len(entries))
202 for _, entry := range entries {
203 jkey := json.ValueOf(entry.key.String())
204 jval, err := o.marshalSingular(entry.value, valType)
205 if !nerr.Merge(err) {
206 return json.Value{}, err
207 }
208 values = append(values, [2]json.Value{jkey, jval})
209 }
210 return json.ValueOf(values), nerr.E
211}
212
213// sortMap orders list based on value of key field for deterministic output.
214func sortMap(keyKind pref.Kind, values []mapEntry) {
215 less := func(i, j int) bool {
216 return values[i].key.String() < values[j].key.String()
217 }
218 switch keyKind {
219 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind,
220 pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
221 less = func(i, j int) bool {
222 return values[i].key.Int() < values[j].key.Int()
223 }
224 case pref.Uint32Kind, pref.Fixed32Kind,
225 pref.Uint64Kind, pref.Fixed64Kind:
226 less = func(i, j int) bool {
227 return values[i].key.Uint() < values[j].key.Uint()
228 }
229 }
230 sort.Slice(values, less)
231}