json.go

  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}