json.go raw

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