build.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // Package jsonutil provides JSON serialisation of AWS requests and responses.
  2. package jsonutil
  3. import (
  4. "bytes"
  5. "encoding/base64"
  6. "fmt"
  7. "reflect"
  8. "sort"
  9. "strconv"
  10. "strings"
  11. "time"
  12. )
  13. // BuildJSON builds a JSON string for a given object v.
  14. func BuildJSON(v interface{}) ([]byte, error) {
  15. var buf bytes.Buffer
  16. err := buildAny(reflect.ValueOf(v), &buf, "")
  17. return buf.Bytes(), err
  18. }
  19. func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
  20. value = reflect.Indirect(value)
  21. if !value.IsValid() {
  22. return nil
  23. }
  24. vtype := value.Type()
  25. t := tag.Get("type")
  26. if t == "" {
  27. switch vtype.Kind() {
  28. case reflect.Struct:
  29. // also it can't be a time object
  30. if _, ok := value.Interface().(time.Time); !ok {
  31. t = "structure"
  32. }
  33. case reflect.Slice:
  34. // also it can't be a byte slice
  35. if _, ok := value.Interface().([]byte); !ok {
  36. t = "list"
  37. }
  38. case reflect.Map:
  39. t = "map"
  40. }
  41. }
  42. switch t {
  43. case "structure":
  44. if field, ok := vtype.FieldByName("SDKShapeTraits"); ok {
  45. tag = field.Tag
  46. }
  47. return buildStruct(value, buf, tag)
  48. case "list":
  49. return buildList(value, buf, tag)
  50. case "map":
  51. return buildMap(value, buf, tag)
  52. default:
  53. return buildScalar(value, buf, tag)
  54. }
  55. }
  56. func buildStruct(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
  57. if !value.IsValid() {
  58. return nil
  59. }
  60. buf.WriteString("{")
  61. t, fields := value.Type(), []*reflect.StructField{}
  62. for i := 0; i < t.NumField(); i++ {
  63. field := t.Field(i)
  64. member := value.FieldByName(field.Name)
  65. if (member.Kind() == reflect.Ptr || member.Kind() == reflect.Slice || member.Kind() == reflect.Map) && member.IsNil() {
  66. continue // ignore unset fields
  67. }
  68. if c := field.Name[0:1]; strings.ToLower(c) == c {
  69. continue // ignore unexported fields
  70. }
  71. if field.Tag.Get("location") != "" {
  72. continue // ignore non-body elements
  73. }
  74. fields = append(fields, &field)
  75. }
  76. for i, field := range fields {
  77. member := value.FieldByName(field.Name)
  78. // figure out what this field is called
  79. name := field.Name
  80. if locName := field.Tag.Get("locationName"); locName != "" {
  81. name = locName
  82. }
  83. buf.WriteString(fmt.Sprintf("%q:", name))
  84. err := buildAny(member, buf, field.Tag)
  85. if err != nil {
  86. return err
  87. }
  88. if i < len(fields)-1 {
  89. buf.WriteString(",")
  90. }
  91. }
  92. buf.WriteString("}")
  93. return nil
  94. }
  95. func buildList(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
  96. buf.WriteString("[")
  97. for i := 0; i < value.Len(); i++ {
  98. buildAny(value.Index(i), buf, "")
  99. if i < value.Len()-1 {
  100. buf.WriteString(",")
  101. }
  102. }
  103. buf.WriteString("]")
  104. return nil
  105. }
  106. func buildMap(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
  107. buf.WriteString("{")
  108. keys := make([]string, value.Len())
  109. for i, n := range value.MapKeys() {
  110. keys[i] = n.String()
  111. }
  112. sort.Strings(keys)
  113. for i, k := range keys {
  114. buf.WriteString(fmt.Sprintf("%q:", k))
  115. buildAny(value.MapIndex(reflect.ValueOf(k)), buf, "")
  116. if i < len(keys)-1 {
  117. buf.WriteString(",")
  118. }
  119. }
  120. buf.WriteString("}")
  121. return nil
  122. }
  123. func buildScalar(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
  124. switch converted := value.Interface().(type) {
  125. case string:
  126. writeString(converted, buf)
  127. case []byte:
  128. if !value.IsNil() {
  129. buf.WriteString(fmt.Sprintf("%q", base64.StdEncoding.EncodeToString(converted)))
  130. }
  131. case bool:
  132. buf.WriteString(strconv.FormatBool(converted))
  133. case int64:
  134. buf.WriteString(strconv.FormatInt(converted, 10))
  135. case float64:
  136. buf.WriteString(strconv.FormatFloat(converted, 'f', -1, 64))
  137. case time.Time:
  138. buf.WriteString(strconv.FormatInt(converted.UTC().Unix(), 10))
  139. default:
  140. return fmt.Errorf("unsupported JSON value %v (%s)", value.Interface(), value.Type())
  141. }
  142. return nil
  143. }
  144. func writeString(s string, buf *bytes.Buffer) {
  145. buf.WriteByte('"')
  146. for _, r := range s {
  147. if r == '"' {
  148. buf.WriteString(`\"`)
  149. } else if r == '\\' {
  150. buf.WriteString(`\\`)
  151. } else if r == '\b' {
  152. buf.WriteString(`\b`)
  153. } else if r == '\f' {
  154. buf.WriteString(`\f`)
  155. } else if r == '\r' {
  156. buf.WriteString(`\r`)
  157. } else if r == '\t' {
  158. buf.WriteString(`\t`)
  159. } else if r == '\n' {
  160. buf.WriteString(`\n`)
  161. } else if r < 32 {
  162. fmt.Fprintf(buf, "\\u%0.4x", r)
  163. } else {
  164. buf.WriteRune(r)
  165. }
  166. }
  167. buf.WriteByte('"')
  168. }