1// Package jsonutil provides JSON serialization of AWS requests and responses. 2package jsonutil 3 4import ( 5 "bytes" 6 "encoding/base64" 7 "encoding/json" 8 "fmt" 9 "math" 10 "reflect" 11 "sort" 12 "strconv" 13 "time" 14 15 "github.com/aws/aws-sdk-go/aws" 16 "github.com/aws/aws-sdk-go/private/protocol" 17) 18 19var timeType = reflect.ValueOf(time.Time{}).Type() 20var byteSliceType = reflect.ValueOf([]byte{}).Type() 21 22// BuildJSON builds a JSON string for a given object v. 23func BuildJSON(v interface{}) ([]byte, error) { 24 var buf bytes.Buffer 25 26 err := buildAny(reflect.ValueOf(v), &buf, "") 27 return buf.Bytes(), err 28} 29 30func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { 31 origVal := value 32 value = reflect.Indirect(value) 33 if !value.IsValid() { 34 return nil 35 } 36 37 vtype := value.Type() 38 39 t := tag.Get("type") 40 if t == "" { 41 switch vtype.Kind() { 42 case reflect.Struct: 43 // also it can't be a time object 44 if value.Type() != timeType { 45 t = "structure" 46 } 47 case reflect.Slice: 48 // also it can't be a byte slice 49 if _, ok := value.Interface().([]byte); !ok { 50 t = "list" 51 } 52 case reflect.Map: 53 // cannot be a JSONValue map 54 if _, ok := value.Interface().(aws.JSONValue); !ok { 55 t = "map" 56 } 57 } 58 } 59 60 switch t { 61 case "structure": 62 if field, ok := vtype.FieldByName("_"); ok { 63 tag = field.Tag 64 } 65 return buildStruct(value, buf, tag) 66 case "list": 67 return buildList(value, buf, tag) 68 case "map": 69 return buildMap(value, buf, tag) 70 default: 71 return buildScalar(origVal, buf, tag) 72 } 73} 74 75func buildStruct(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { 76 if !value.IsValid() { 77 return nil 78 } 79 80 // unwrap payloads 81 if payload := tag.Get("payload"); payload != "" { 82 field, _ := value.Type().FieldByName(payload) 83 tag = field.Tag 84 value = elemOf(value.FieldByName(payload)) 85 86 if !value.IsValid() { 87 return nil 88 } 89 } 90 91 buf.WriteByte('{') 92 93 t := value.Type() 94 first := true 95 for i := 0; i < t.NumField(); i++ { 96 member := value.Field(i) 97 98 // This allocates the most memory. 99 // Additionally, we cannot skip nil fields due to 100 // idempotency auto filling. 101 field := t.Field(i) 102 103 if field.PkgPath != "" { 104 continue // ignore unexported fields 105 } 106 if field.Tag.Get("json") == "-" { 107 continue 108 } 109 if field.Tag.Get("location") != "" { 110 continue // ignore non-body elements 111 } 112 if field.Tag.Get("ignore") != "" { 113 continue 114 } 115 116 if protocol.CanSetIdempotencyToken(member, field) { 117 token := protocol.GetIdempotencyToken() 118 member = reflect.ValueOf(&token) 119 } 120 121 if (member.Kind() == reflect.Ptr || member.Kind() == reflect.Slice || member.Kind() == reflect.Map) && member.IsNil() { 122 continue // ignore unset fields 123 } 124 125 if first { 126 first = false 127 } else { 128 buf.WriteByte(',') 129 } 130 131 // figure out what this field is called 132 name := field.Name 133 if locName := field.Tag.Get("locationName"); locName != "" { 134 name = locName 135 } 136 137 writeString(name, buf) 138 buf.WriteString(`:`) 139 140 err := buildAny(member, buf, field.Tag) 141 if err != nil { 142 return err 143 } 144 145 } 146 147 buf.WriteString("}") 148 149 return nil 150} 151 152func buildList(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { 153 buf.WriteString("[") 154 155 for i := 0; i < value.Len(); i++ { 156 buildAny(value.Index(i), buf, "") 157 158 if i < value.Len()-1 { 159 buf.WriteString(",") 160 } 161 } 162 163 buf.WriteString("]") 164 165 return nil 166} 167 168type sortedValues []reflect.Value 169 170func (sv sortedValues) Len() int { return len(sv) } 171func (sv sortedValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } 172func (sv sortedValues) Less(i, j int) bool { return sv[i].String() < sv[j].String() } 173 174func buildMap(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { 175 buf.WriteString("{") 176 177 sv := sortedValues(value.MapKeys()) 178 sort.Sort(sv) 179 180 for i, k := range sv { 181 if i > 0 { 182 buf.WriteByte(',') 183 } 184 185 writeString(k.String(), buf) 186 buf.WriteString(`:`) 187 188 buildAny(value.MapIndex(k), buf, "") 189 } 190 191 buf.WriteString("}") 192 193 return nil 194} 195 196func buildScalar(v reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { 197 // prevents allocation on the heap. 198 scratch := [64]byte{} 199 switch value := reflect.Indirect(v); value.Kind() { 200 case reflect.String: 201 writeString(value.String(), buf) 202 case reflect.Bool: 203 if value.Bool() { 204 buf.WriteString("true") 205 } else { 206 buf.WriteString("false") 207 } 208 case reflect.Int64: 209 buf.Write(strconv.AppendInt(scratch[:0], value.Int(), 10)) 210 case reflect.Float64: 211 f := value.Float() 212 if math.IsInf(f, 0) || math.IsNaN(f) { 213 return &json.UnsupportedValueError{Value: v, Str: strconv.FormatFloat(f, 'f', -1, 64)} 214 } 215 buf.Write(strconv.AppendFloat(scratch[:0], f, 'f', -1, 64)) 216 default: 217 switch converted := value.Interface().(type) { 218 case time.Time: 219 format := tag.Get("timestampFormat") 220 if len(format) == 0 { 221 format = protocol.UnixTimeFormatName 222 } 223 224 ts := protocol.FormatTime(format, converted) 225 if format != protocol.UnixTimeFormatName { 226 ts = `"` + ts + `"` 227 } 228 229 buf.WriteString(ts) 230 case []byte: 231 if !value.IsNil() { 232 buf.WriteByte('"') 233 if len(converted) < 1024 { 234 // for small buffers, using Encode directly is much faster. 235 dst := make([]byte, base64.StdEncoding.EncodedLen(len(converted))) 236 base64.StdEncoding.Encode(dst, converted) 237 buf.Write(dst) 238 } else { 239 // for large buffers, avoid unnecessary extra temporary 240 // buffer space. 241 enc := base64.NewEncoder(base64.StdEncoding, buf) 242 enc.Write(converted) 243 enc.Close() 244 } 245 buf.WriteByte('"') 246 } 247 case aws.JSONValue: 248 str, err := protocol.EncodeJSONValue(converted, protocol.QuotedEscape) 249 if err != nil { 250 return fmt.Errorf("unable to encode JSONValue, %v", err) 251 } 252 buf.WriteString(str) 253 default: 254 return fmt.Errorf("unsupported JSON value %v (%s)", value.Interface(), value.Type()) 255 } 256 } 257 return nil 258} 259 260var hex = "0123456789abcdef" 261 262func writeString(s string, buf *bytes.Buffer) { 263 buf.WriteByte('"') 264 for i := 0; i < len(s); i++ { 265 if s[i] == '"' { 266 buf.WriteString(`\"`) 267 } else if s[i] == '\\' { 268 buf.WriteString(`\\`) 269 } else if s[i] == '\b' { 270 buf.WriteString(`\b`) 271 } else if s[i] == '\f' { 272 buf.WriteString(`\f`) 273 } else if s[i] == '\r' { 274 buf.WriteString(`\r`) 275 } else if s[i] == '\t' { 276 buf.WriteString(`\t`) 277 } else if s[i] == '\n' { 278 buf.WriteString(`\n`) 279 } else if s[i] < 32 { 280 buf.WriteString("\\u00") 281 buf.WriteByte(hex[s[i]>>4]) 282 buf.WriteByte(hex[s[i]&0xF]) 283 } else { 284 buf.WriteByte(s[i]) 285 } 286 } 287 buf.WriteByte('"') 288} 289 290// Returns the reflection element of a value, if it is a pointer. 291func elemOf(value reflect.Value) reflect.Value { 292 for value.Kind() == reflect.Ptr { 293 value = value.Elem() 294 } 295 return value 296} 297