json.go 4.9 KB

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