Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

212 řádky
6.0 KiB

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