json.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. // Copyright 2015 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. package gensupport
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "reflect"
  9. "strings"
  10. )
  11. // MarshalJSON returns a JSON encoding of schema containing only selected fields.
  12. // A field is selected if any of the following is true:
  13. // - it has a non-empty value
  14. // - its field name is present in forceSendFields and it is not a nil pointer or nil interface
  15. // - its field name is present in nullFields.
  16. //
  17. // The JSON key for each selected field is taken from the field's json: struct tag.
  18. func MarshalJSON(schema interface{}, forceSendFields, nullFields []string) ([]byte, error) {
  19. if len(forceSendFields) == 0 && len(nullFields) == 0 {
  20. return json.Marshal(schema)
  21. }
  22. mustInclude := make(map[string]bool)
  23. for _, f := range forceSendFields {
  24. mustInclude[f] = true
  25. }
  26. useNull := make(map[string]bool)
  27. useNullMaps := make(map[string]map[string]bool)
  28. for _, nf := range nullFields {
  29. parts := strings.SplitN(nf, ".", 2)
  30. field := parts[0]
  31. if len(parts) == 1 {
  32. useNull[field] = true
  33. } else {
  34. if useNullMaps[field] == nil {
  35. useNullMaps[field] = map[string]bool{}
  36. }
  37. useNullMaps[field][parts[1]] = true
  38. }
  39. }
  40. dataMap, err := schemaToMap(schema, mustInclude, useNull, useNullMaps)
  41. if err != nil {
  42. return nil, err
  43. }
  44. return json.Marshal(dataMap)
  45. }
  46. func schemaToMap(schema interface{}, mustInclude, useNull map[string]bool, useNullMaps map[string]map[string]bool) (map[string]interface{}, error) {
  47. m := make(map[string]interface{})
  48. s := reflect.ValueOf(schema)
  49. st := s.Type()
  50. for i := 0; i < s.NumField(); i++ {
  51. jsonTag := st.Field(i).Tag.Get("json")
  52. if jsonTag == "" {
  53. continue
  54. }
  55. tag, err := parseJSONTag(jsonTag)
  56. if err != nil {
  57. return nil, err
  58. }
  59. if tag.ignore {
  60. continue
  61. }
  62. v := s.Field(i)
  63. f := st.Field(i)
  64. if useNull[f.Name] {
  65. if !isEmptyValue(v) {
  66. return nil, fmt.Errorf("field %q in NullFields has non-empty value", f.Name)
  67. }
  68. m[tag.apiName] = nil
  69. continue
  70. }
  71. if !includeField(v, f, mustInclude) {
  72. continue
  73. }
  74. // If map fields are explicitly set to null, use a map[string]interface{}.
  75. if f.Type.Kind() == reflect.Map && useNullMaps[f.Name] != nil {
  76. ms, ok := v.Interface().(map[string]string)
  77. if !ok {
  78. mi, err := initMapSlow(v, f.Name, useNullMaps)
  79. if err != nil {
  80. return nil, err
  81. }
  82. m[tag.apiName] = mi
  83. continue
  84. }
  85. mi := map[string]interface{}{}
  86. for k, v := range ms {
  87. mi[k] = v
  88. }
  89. for k := range useNullMaps[f.Name] {
  90. mi[k] = nil
  91. }
  92. m[tag.apiName] = mi
  93. continue
  94. }
  95. // nil maps are treated as empty maps.
  96. if f.Type.Kind() == reflect.Map && v.IsNil() {
  97. m[tag.apiName] = map[string]string{}
  98. continue
  99. }
  100. // nil slices are treated as empty slices.
  101. if f.Type.Kind() == reflect.Slice && v.IsNil() {
  102. m[tag.apiName] = []bool{}
  103. continue
  104. }
  105. if tag.stringFormat {
  106. m[tag.apiName] = formatAsString(v, f.Type.Kind())
  107. } else {
  108. m[tag.apiName] = v.Interface()
  109. }
  110. }
  111. return m, nil
  112. }
  113. // initMapSlow uses reflection to build up a map object. This is slower than
  114. // the default behavior so it should be used only as a fallback.
  115. func initMapSlow(rv reflect.Value, fieldName string, useNullMaps map[string]map[string]bool) (map[string]interface{}, error) {
  116. mi := map[string]interface{}{}
  117. iter := rv.MapRange()
  118. for iter.Next() {
  119. k, ok := iter.Key().Interface().(string)
  120. if !ok {
  121. return nil, fmt.Errorf("field %q has keys in NullFields but is not a map[string]any", fieldName)
  122. }
  123. v := iter.Value().Interface()
  124. mi[k] = v
  125. }
  126. for k := range useNullMaps[fieldName] {
  127. mi[k] = nil
  128. }
  129. return mi, nil
  130. }
  131. // formatAsString returns a string representation of v, dereferencing it first if possible.
  132. func formatAsString(v reflect.Value, kind reflect.Kind) string {
  133. if kind == reflect.Ptr && !v.IsNil() {
  134. v = v.Elem()
  135. }
  136. return fmt.Sprintf("%v", v.Interface())
  137. }
  138. // jsonTag represents a restricted version of the struct tag format used by encoding/json.
  139. // It is used to describe the JSON encoding of fields in a Schema struct.
  140. type jsonTag struct {
  141. apiName string
  142. stringFormat bool
  143. ignore bool
  144. }
  145. // parseJSONTag parses a restricted version of the struct tag format used by encoding/json.
  146. // The format of the tag must match that generated by the Schema.writeSchemaStruct method
  147. // in the api generator.
  148. func parseJSONTag(val string) (jsonTag, error) {
  149. if val == "-" {
  150. return jsonTag{ignore: true}, nil
  151. }
  152. var tag jsonTag
  153. i := strings.Index(val, ",")
  154. if i == -1 || val[:i] == "" {
  155. return tag, fmt.Errorf("malformed json tag: %s", val)
  156. }
  157. tag = jsonTag{
  158. apiName: val[:i],
  159. }
  160. switch val[i+1:] {
  161. case "omitempty":
  162. case "omitempty,string":
  163. tag.stringFormat = true
  164. default:
  165. return tag, fmt.Errorf("malformed json tag: %s", val)
  166. }
  167. return tag, nil
  168. }
  169. // Reports whether the struct field "f" with value "v" should be included in JSON output.
  170. func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string]bool) bool {
  171. // The regular JSON encoding of a nil pointer is "null", which means "delete this field".
  172. // Therefore, we could enable field deletion by honoring pointer fields' presence in the mustInclude set.
  173. // However, many fields are not pointers, so there would be no way to delete these fields.
  174. // Rather than partially supporting field deletion, we ignore mustInclude for nil pointer fields.
  175. // Deletion will be handled by a separate mechanism.
  176. if f.Type.Kind() == reflect.Ptr && v.IsNil() {
  177. return false
  178. }
  179. // The "any" type is represented as an interface{}. If this interface
  180. // is nil, there is no reasonable representation to send. We ignore
  181. // these fields, for the same reasons as given above for pointers.
  182. if f.Type.Kind() == reflect.Interface && v.IsNil() {
  183. return false
  184. }
  185. return mustInclude[f.Name] || !isEmptyValue(v)
  186. }
  187. // isEmptyValue reports whether v is the empty value for its type. This
  188. // implementation is based on that of the encoding/json package, but its
  189. // correctness does not depend on it being identical. What's important is that
  190. // this function return false in situations where v should not be sent as part
  191. // of a PATCH operation.
  192. func isEmptyValue(v reflect.Value) bool {
  193. switch v.Kind() {
  194. case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
  195. return v.Len() == 0
  196. case reflect.Bool:
  197. return !v.Bool()
  198. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  199. return v.Int() == 0
  200. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  201. return v.Uint() == 0
  202. case reflect.Float32, reflect.Float64:
  203. return v.Float() == 0
  204. case reflect.Interface, reflect.Ptr:
  205. return v.IsNil()
  206. }
  207. return false
  208. }