1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT license.
3
4package json
5
6import (
7 "bytes"
8 "encoding/json"
9 "fmt"
10 "reflect"
11 "unicode"
12)
13
14// marshalStruct takes in i, which must be a *struct or struct and marshals its content
15// as JSON into buff (sometimes with writes to buff directly, sometimes via enc).
16// This call is recursive for all fields of *struct or struct type.
17func marshalStruct(v reflect.Value, buff *bytes.Buffer, enc *json.Encoder) error {
18 if v.Kind() == reflect.Ptr {
19 v = v.Elem()
20 }
21 // We only care about custom Marshalling a struct.
22 if v.Kind() != reflect.Struct {
23 return fmt.Errorf("bug: marshal() received a non *struct or struct, received type %T", v.Interface())
24 }
25
26 if hasMarshalJSON(v) {
27 b, err := callMarshalJSON(v)
28 if err != nil {
29 return err
30 }
31 buff.Write(b)
32 return nil
33 }
34
35 t := v.Type()
36
37 // If it has an AdditionalFields field make sure its the right type.
38 f := v.FieldByName(addField)
39 if f.Kind() != reflect.Invalid {
40 if f.Kind() != reflect.Map {
41 return fmt.Errorf("type %T has field 'AdditionalFields' that is not a map[string]interface{}", v.Interface())
42 }
43 if !f.Type().AssignableTo(mapStrInterType) {
44 return fmt.Errorf("type %T has field 'AdditionalFields' that is not a map[string]interface{}", v.Interface())
45 }
46 }
47
48 translator, err := findFields(v)
49 if err != nil {
50 return err
51 }
52
53 buff.WriteByte(leftBrace)
54 for x := 0; x < v.NumField(); x++ {
55 field := v.Field(x)
56
57 // We don't access private fields.
58 if unicode.IsLower(rune(t.Field(x).Name[0])) {
59 continue
60 }
61
62 if t.Field(x).Name == addField {
63 if v.Field(x).Len() > 0 {
64 if err := writeAddFields(field.Interface(), buff, enc); err != nil {
65 return err
66 }
67 buff.WriteByte(comma)
68 }
69 continue
70 }
71
72 // If they have omitempty set, we don't write out the field if
73 // it is the zero value.
74 if hasOmitEmpty(t.Field(x).Tag.Get("json")) {
75 if v.Field(x).IsZero() {
76 continue
77 }
78 }
79
80 // Write out the field name part.
81 jsonName := translator.jsonName(t.Field(x).Name)
82 buff.WriteString(fmt.Sprintf("%q:", jsonName))
83
84 if field.Kind() == reflect.Ptr {
85 field = field.Elem()
86 }
87
88 if err := marshalStructField(field, buff, enc); err != nil {
89 return err
90 }
91 }
92
93 buff.Truncate(buff.Len() - 1) // Remove final comma
94 buff.WriteByte(rightBrace)
95
96 return nil
97}
98
99func marshalStructField(field reflect.Value, buff *bytes.Buffer, enc *json.Encoder) error {
100 // Determine if we need a trailing comma.
101 defer buff.WriteByte(comma)
102
103 switch field.Kind() {
104 // If it was a *struct or struct, we need to recursively all marshal().
105 case reflect.Struct:
106 if field.CanAddr() {
107 field = field.Addr()
108 }
109 return marshalStruct(field, buff, enc)
110 case reflect.Map:
111 return marshalMap(field, buff, enc)
112 case reflect.Slice:
113 return marshalSlice(field, buff, enc)
114 }
115
116 // It is just a basic type, so encode it.
117 if err := enc.Encode(field.Interface()); err != nil {
118 return err
119 }
120 buff.Truncate(buff.Len() - 1) // Remove Encode() added \n
121
122 return nil
123}
124
125func marshalMap(v reflect.Value, buff *bytes.Buffer, enc *json.Encoder) error {
126 if v.Kind() != reflect.Map {
127 return fmt.Errorf("bug: marshalMap() called on %T", v.Interface())
128 }
129 if v.Len() == 0 {
130 buff.WriteByte(leftBrace)
131 buff.WriteByte(rightBrace)
132 return nil
133 }
134 encoder := mapEncode{m: v, buff: buff, enc: enc}
135 return encoder.run()
136}
137
138type mapEncode struct {
139 m reflect.Value
140 buff *bytes.Buffer
141 enc *json.Encoder
142
143 valueBaseType reflect.Type
144}
145
146// run runs our encoder state machine.
147func (m *mapEncode) run() error {
148 var state = m.start
149 var err error
150 for {
151 state, err = state()
152 if err != nil {
153 return err
154 }
155 if state == nil {
156 return nil
157 }
158 }
159}
160
161func (m *mapEncode) start() (stateFn, error) {
162 if hasMarshalJSON(m.m) {
163 b, err := callMarshalJSON(m.m)
164 if err != nil {
165 return nil, err
166 }
167 m.buff.Write(b)
168 return nil, nil
169 }
170
171 valueBaseType := m.m.Type().Elem()
172 if valueBaseType.Kind() == reflect.Ptr {
173 valueBaseType = valueBaseType.Elem()
174 }
175 m.valueBaseType = valueBaseType
176
177 switch valueBaseType.Kind() {
178 case reflect.Ptr:
179 return nil, fmt.Errorf("Marshal does not support **<type> or *<reference>")
180 case reflect.Struct, reflect.Map, reflect.Slice:
181 return m.encode, nil
182 }
183
184 // If the map value doesn't have a struct/map/slice, just Encode() it.
185 if err := m.enc.Encode(m.m.Interface()); err != nil {
186 return nil, err
187 }
188 m.buff.Truncate(m.buff.Len() - 1) // Remove Encode() added \n
189 return nil, nil
190}
191
192func (m *mapEncode) encode() (stateFn, error) {
193 m.buff.WriteByte(leftBrace)
194
195 iter := m.m.MapRange()
196 for iter.Next() {
197 // Write the key.
198 k := iter.Key()
199 m.buff.WriteString(fmt.Sprintf("%q:", k.String()))
200
201 v := iter.Value()
202 switch m.valueBaseType.Kind() {
203 case reflect.Struct:
204 if v.CanAddr() {
205 v = v.Addr()
206 }
207 if err := marshalStruct(v, m.buff, m.enc); err != nil {
208 return nil, err
209 }
210 case reflect.Map:
211 if err := marshalMap(v, m.buff, m.enc); err != nil {
212 return nil, err
213 }
214 case reflect.Slice:
215 if err := marshalSlice(v, m.buff, m.enc); err != nil {
216 return nil, err
217 }
218 default:
219 panic(fmt.Sprintf("critical bug: mapEncode.encode() called with value base type: %v", m.valueBaseType.Kind()))
220 }
221 m.buff.WriteByte(comma)
222 }
223 m.buff.Truncate(m.buff.Len() - 1) // Remove final comma
224 m.buff.WriteByte(rightBrace)
225
226 return nil, nil
227}
228
229func marshalSlice(v reflect.Value, buff *bytes.Buffer, enc *json.Encoder) error {
230 if v.Kind() != reflect.Slice {
231 return fmt.Errorf("bug: marshalSlice() called on %T", v.Interface())
232 }
233 if v.Len() == 0 {
234 buff.WriteByte(leftParen)
235 buff.WriteByte(rightParen)
236 return nil
237 }
238 encoder := sliceEncode{s: v, buff: buff, enc: enc}
239 return encoder.run()
240}
241
242type sliceEncode struct {
243 s reflect.Value
244 buff *bytes.Buffer
245 enc *json.Encoder
246
247 valueBaseType reflect.Type
248}
249
250// run runs our encoder state machine.
251func (s *sliceEncode) run() error {
252 var state = s.start
253 var err error
254 for {
255 state, err = state()
256 if err != nil {
257 return err
258 }
259 if state == nil {
260 return nil
261 }
262 }
263}
264
265func (s *sliceEncode) start() (stateFn, error) {
266 if hasMarshalJSON(s.s) {
267 b, err := callMarshalJSON(s.s)
268 if err != nil {
269 return nil, err
270 }
271 s.buff.Write(b)
272 return nil, nil
273 }
274
275 valueBaseType := s.s.Type().Elem()
276 if valueBaseType.Kind() == reflect.Ptr {
277 valueBaseType = valueBaseType.Elem()
278 }
279 s.valueBaseType = valueBaseType
280
281 switch valueBaseType.Kind() {
282 case reflect.Ptr:
283 return nil, fmt.Errorf("Marshal does not support **<type> or *<reference>")
284 case reflect.Struct, reflect.Map, reflect.Slice:
285 return s.encode, nil
286 }
287
288 // If the map value doesn't have a struct/map/slice, just Encode() it.
289 if err := s.enc.Encode(s.s.Interface()); err != nil {
290 return nil, err
291 }
292 s.buff.Truncate(s.buff.Len() - 1) // Remove Encode added \n
293
294 return nil, nil
295}
296
297func (s *sliceEncode) encode() (stateFn, error) {
298 s.buff.WriteByte(leftParen)
299 for i := 0; i < s.s.Len(); i++ {
300 v := s.s.Index(i)
301 switch s.valueBaseType.Kind() {
302 case reflect.Struct:
303 if v.CanAddr() {
304 v = v.Addr()
305 }
306 if err := marshalStruct(v, s.buff, s.enc); err != nil {
307 return nil, err
308 }
309 case reflect.Map:
310 if err := marshalMap(v, s.buff, s.enc); err != nil {
311 return nil, err
312 }
313 case reflect.Slice:
314 if err := marshalSlice(v, s.buff, s.enc); err != nil {
315 return nil, err
316 }
317 default:
318 panic(fmt.Sprintf("critical bug: mapEncode.encode() called with value base type: %v", s.valueBaseType.Kind()))
319 }
320 s.buff.WriteByte(comma)
321 }
322 s.buff.Truncate(s.buff.Len() - 1) // Remove final comma
323 s.buff.WriteByte(rightParen)
324 return nil, nil
325}
326
327// writeAddFields writes the AdditionalFields struct field out to JSON as field
328// values. i must be a map[string]interface{} or this will panic.
329func writeAddFields(i interface{}, buff *bytes.Buffer, enc *json.Encoder) error {
330 m := i.(map[string]interface{})
331
332 x := 0
333 for k, v := range m {
334 buff.WriteString(fmt.Sprintf("%q:", k))
335 if err := enc.Encode(v); err != nil {
336 return err
337 }
338 buff.Truncate(buff.Len() - 1) // Remove Encode() added \n
339
340 if x+1 != len(m) {
341 buff.WriteByte(comma)
342 }
343 x++
344 }
345 return nil
346}