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