1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT license.
3
4// Package json provide functions for marshalling an unmarshalling types to JSON. These functions are meant to
5// be utilized inside of structs that implement json.Unmarshaler and json.Marshaler interfaces.
6// This package provides the additional functionality of writing fields that are not in the struct when marshalling
7// to a field called AdditionalFields if that field exists and is a map[string]interface{}.
8// When marshalling, if the struct has all the same prerequisites, it will uses the keys in AdditionalFields as
9// extra fields. This package uses encoding/json underneath.
10package json
11
12import (
13 "bytes"
14 "encoding/json"
15 "fmt"
16 "reflect"
17 "strings"
18)
19
20const addField = "AdditionalFields"
21const (
22 marshalJSON = "MarshalJSON"
23 unmarshalJSON = "UnmarshalJSON"
24)
25
26var (
27 leftBrace = []byte("{")[0]
28 rightBrace = []byte("}")[0]
29 comma = []byte(",")[0]
30 leftParen = []byte("[")[0]
31 rightParen = []byte("]")[0]
32)
33
34var mapStrInterType = reflect.TypeOf(map[string]interface{}{})
35
36// stateFn defines a state machine function. This will be used in all state
37// machines in this package.
38type stateFn func() (stateFn, error)
39
40// Marshal is used to marshal a type into its JSON representation. It
41// wraps the stdlib calls in order to marshal a struct or *struct so
42// that a field called "AdditionalFields" of type map[string]interface{}
43// with "-" used inside struct tag `json:"-"` can be marshalled as if
44// they were fields within the struct.
45func Marshal(i interface{}) ([]byte, error) {
46 buff := bytes.Buffer{}
47 enc := json.NewEncoder(&buff)
48 enc.SetEscapeHTML(false)
49 enc.SetIndent("", "")
50
51 v := reflect.ValueOf(i)
52 if v.Kind() != reflect.Ptr && v.CanAddr() {
53 v = v.Addr()
54 }
55 err := marshalStruct(v, &buff, enc)
56 if err != nil {
57 return nil, err
58 }
59 return buff.Bytes(), nil
60}
61
62// Unmarshal unmarshals a []byte representing JSON into i, which must be a *struct. In addition, if the struct has
63// a field called AdditionalFields of type map[string]interface{}, JSON data representing fields not in the struct
64// will be written as key/value pairs to AdditionalFields.
65func Unmarshal(b []byte, i interface{}) error {
66 if len(b) == 0 {
67 return nil
68 }
69
70 jdec := json.NewDecoder(bytes.NewBuffer(b))
71 jdec.UseNumber()
72 return unmarshalStruct(jdec, i)
73}
74
75// MarshalRaw marshals i into a json.RawMessage. If I cannot be marshalled,
76// this will panic. This is exposed to help test AdditionalField values
77// which are stored as json.RawMessage.
78func MarshalRaw(i interface{}) json.RawMessage {
79 b, err := json.Marshal(i)
80 if err != nil {
81 panic(err)
82 }
83 return json.RawMessage(b)
84}
85
86// isDelim simply tests to see if a json.Token is a delimeter.
87func isDelim(got json.Token) bool {
88 switch got.(type) {
89 case json.Delim:
90 return true
91 }
92 return false
93}
94
95// delimIs tests got to see if it is want.
96func delimIs(got json.Token, want rune) bool {
97 switch v := got.(type) {
98 case json.Delim:
99 if v == json.Delim(want) {
100 return true
101 }
102 }
103 return false
104}
105
106// hasMarshalJSON will determine if the value or a pointer to this value has
107// the MarshalJSON method.
108func hasMarshalJSON(v reflect.Value) bool {
109 if method := v.MethodByName(marshalJSON); method.Kind() != reflect.Invalid {
110 _, ok := v.Interface().(json.Marshaler)
111 return ok
112 }
113
114 if v.Kind() == reflect.Ptr {
115 v = v.Elem()
116 } else {
117 if !v.CanAddr() {
118 return false
119 }
120 v = v.Addr()
121 }
122
123 if method := v.MethodByName(marshalJSON); method.Kind() != reflect.Invalid {
124 _, ok := v.Interface().(json.Marshaler)
125 return ok
126 }
127 return false
128}
129
130// callMarshalJSON will call MarshalJSON() method on the value or a pointer to this value.
131// This will panic if the method is not defined.
132func callMarshalJSON(v reflect.Value) ([]byte, error) {
133 if method := v.MethodByName(marshalJSON); method.Kind() != reflect.Invalid {
134 marsh := v.Interface().(json.Marshaler)
135 return marsh.MarshalJSON()
136 }
137
138 if v.Kind() == reflect.Ptr {
139 v = v.Elem()
140 } else {
141 if v.CanAddr() {
142 v = v.Addr()
143 }
144 }
145
146 if method := v.MethodByName(unmarshalJSON); method.Kind() != reflect.Invalid {
147 marsh := v.Interface().(json.Marshaler)
148 return marsh.MarshalJSON()
149 }
150
151 panic(fmt.Sprintf("callMarshalJSON called on type %T that does not have MarshalJSON defined", v.Interface()))
152}
153
154// hasUnmarshalJSON will determine if the value or a pointer to this value has
155// the UnmarshalJSON method.
156func hasUnmarshalJSON(v reflect.Value) bool {
157 // You can't unmarshal on a non-pointer type.
158 if v.Kind() != reflect.Ptr {
159 if !v.CanAddr() {
160 return false
161 }
162 v = v.Addr()
163 }
164
165 if method := v.MethodByName(unmarshalJSON); method.Kind() != reflect.Invalid {
166 _, ok := v.Interface().(json.Unmarshaler)
167 return ok
168 }
169
170 return false
171}
172
173// hasOmitEmpty indicates if the field has instructed us to not output
174// the field if omitempty is set on the tag. tag is the string
175// returned by reflect.StructField.Tag().Get().
176func hasOmitEmpty(tag string) bool {
177 sl := strings.Split(tag, ",")
178 for _, str := range sl {
179 if str == "omitempty" {
180 return true
181 }
182 }
183 return false
184}