github.com/aavshr/aws-sdk-go@v1.41.3/service/dynamodb/dynamodbattribute/converter.go (about) 1 package dynamodbattribute 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "reflect" 8 "runtime" 9 "strconv" 10 11 "github.com/aavshr/aws-sdk-go/aws/awserr" 12 "github.com/aavshr/aws-sdk-go/service/dynamodb" 13 ) 14 15 // ConvertToMap accepts a map[string]interface{} or struct and converts it to a 16 // map[string]*dynamodb.AttributeValue. 17 // 18 // If in contains any structs, it is first JSON encoded/decoded it to convert it 19 // to a map[string]interface{}, so `json` struct tags are respected. 20 // 21 // Deprecated: Use MarshalMap instead 22 func ConvertToMap(in interface{}) (item map[string]*dynamodb.AttributeValue, err error) { 23 defer func() { 24 if r := recover(); r != nil { 25 if e, ok := r.(runtime.Error); ok { 26 err = e 27 } else if s, ok := r.(string); ok { 28 err = fmt.Errorf(s) 29 } else { 30 err = r.(error) 31 } 32 item = nil 33 } 34 }() 35 36 if in == nil { 37 return nil, awserr.New("SerializationError", 38 "in must be a map[string]interface{} or struct, got <nil>", nil) 39 } 40 41 v := reflect.ValueOf(in) 42 if v.Kind() != reflect.Struct && !(v.Kind() == reflect.Map && v.Type().Key().Kind() == reflect.String) { 43 return nil, awserr.New("SerializationError", 44 fmt.Sprintf("in must be a map[string]interface{} or struct, got %s", 45 v.Type().String()), 46 nil) 47 } 48 49 if isTyped(reflect.TypeOf(in)) { 50 var out map[string]interface{} 51 in = convertToUntyped(in, out) 52 } 53 54 item = make(map[string]*dynamodb.AttributeValue) 55 for k, v := range in.(map[string]interface{}) { 56 item[k] = convertTo(v) 57 } 58 59 return item, nil 60 } 61 62 // ConvertFromMap accepts a map[string]*dynamodb.AttributeValue and converts it to a 63 // map[string]interface{} or struct. 64 // 65 // If v points to a struct, the result is first converted it to a 66 // map[string]interface{}, then JSON encoded/decoded it to convert to a struct, 67 // so `json` struct tags are respected. 68 // 69 // Deprecated: Use UnmarshalMap instead 70 func ConvertFromMap(item map[string]*dynamodb.AttributeValue, v interface{}) (err error) { 71 defer func() { 72 if r := recover(); r != nil { 73 if e, ok := r.(runtime.Error); ok { 74 err = e 75 } else if s, ok := r.(string); ok { 76 err = fmt.Errorf(s) 77 } else { 78 err = r.(error) 79 } 80 item = nil 81 } 82 }() 83 84 rv := reflect.ValueOf(v) 85 if rv.Kind() != reflect.Ptr || rv.IsNil() { 86 return awserr.New("SerializationError", 87 fmt.Sprintf("v must be a non-nil pointer to a map[string]interface{} or struct, got %s", 88 rv.Type()), 89 nil) 90 } 91 if rv.Elem().Kind() != reflect.Struct && !(rv.Elem().Kind() == reflect.Map && rv.Elem().Type().Key().Kind() == reflect.String) { 92 return awserr.New("SerializationError", 93 fmt.Sprintf("v must be a non-nil pointer to a map[string]interface{} or struct, got %s", 94 rv.Type()), 95 nil) 96 } 97 98 m := make(map[string]interface{}) 99 for k, v := range item { 100 m[k] = convertFrom(v) 101 } 102 103 if isTyped(reflect.TypeOf(v)) { 104 err = convertToTyped(m, v) 105 } else { 106 rv.Elem().Set(reflect.ValueOf(m)) 107 } 108 109 return err 110 } 111 112 // ConvertToList accepts an array or slice and converts it to a 113 // []*dynamodb.AttributeValue. 114 // 115 // Converting []byte fields to dynamodb.AttributeValue are only currently supported 116 // if the input is a map[string]interface{} type. []byte within typed structs are not 117 // converted correctly and are converted into base64 strings. This is a known bug, 118 // and will be fixed in a later release. 119 // 120 // If in contains any structs, it is first JSON encoded/decoded it to convert it 121 // to a []interface{}, so `json` struct tags are respected. 122 // 123 // Deprecated: Use MarshalList instead 124 func ConvertToList(in interface{}) (item []*dynamodb.AttributeValue, err error) { 125 defer func() { 126 if r := recover(); r != nil { 127 if e, ok := r.(runtime.Error); ok { 128 err = e 129 } else if s, ok := r.(string); ok { 130 err = fmt.Errorf(s) 131 } else { 132 err = r.(error) 133 } 134 item = nil 135 } 136 }() 137 138 if in == nil { 139 return nil, awserr.New("SerializationError", 140 "in must be an array or slice, got <nil>", 141 nil) 142 } 143 144 v := reflect.ValueOf(in) 145 if v.Kind() != reflect.Array && v.Kind() != reflect.Slice { 146 return nil, awserr.New("SerializationError", 147 fmt.Sprintf("in must be an array or slice, got %s", 148 v.Type().String()), 149 nil) 150 } 151 152 if isTyped(reflect.TypeOf(in)) { 153 var out []interface{} 154 in = convertToUntyped(in, out) 155 } 156 157 item = make([]*dynamodb.AttributeValue, 0, len(in.([]interface{}))) 158 for _, v := range in.([]interface{}) { 159 item = append(item, convertTo(v)) 160 } 161 162 return item, nil 163 } 164 165 // ConvertFromList accepts a []*dynamodb.AttributeValue and converts it to an array or 166 // slice. 167 // 168 // If v contains any structs, the result is first converted it to a 169 // []interface{}, then JSON encoded/decoded it to convert to a typed array or 170 // slice, so `json` struct tags are respected. 171 // 172 // Deprecated: Use UnmarshalList instead 173 func ConvertFromList(item []*dynamodb.AttributeValue, v interface{}) (err error) { 174 defer func() { 175 if r := recover(); r != nil { 176 if e, ok := r.(runtime.Error); ok { 177 err = e 178 } else if s, ok := r.(string); ok { 179 err = fmt.Errorf(s) 180 } else { 181 err = r.(error) 182 } 183 item = nil 184 } 185 }() 186 187 rv := reflect.ValueOf(v) 188 if rv.Kind() != reflect.Ptr || rv.IsNil() { 189 return awserr.New("SerializationError", 190 fmt.Sprintf("v must be a non-nil pointer to an array or slice, got %s", 191 rv.Type()), 192 nil) 193 } 194 if rv.Elem().Kind() != reflect.Array && rv.Elem().Kind() != reflect.Slice { 195 return awserr.New("SerializationError", 196 fmt.Sprintf("v must be a non-nil pointer to an array or slice, got %s", 197 rv.Type()), 198 nil) 199 } 200 201 l := make([]interface{}, 0, len(item)) 202 for _, v := range item { 203 l = append(l, convertFrom(v)) 204 } 205 206 if isTyped(reflect.TypeOf(v)) { 207 err = convertToTyped(l, v) 208 } else { 209 rv.Elem().Set(reflect.ValueOf(l)) 210 } 211 212 return err 213 } 214 215 // ConvertTo accepts any interface{} and converts it to a *dynamodb.AttributeValue. 216 // 217 // If in contains any structs, it is first JSON encoded/decoded it to convert it 218 // to a interface{}, so `json` struct tags are respected. 219 // 220 // Deprecated: Use Marshal instead 221 func ConvertTo(in interface{}) (item *dynamodb.AttributeValue, err error) { 222 defer func() { 223 if r := recover(); r != nil { 224 if e, ok := r.(runtime.Error); ok { 225 err = e 226 } else if s, ok := r.(string); ok { 227 err = fmt.Errorf(s) 228 } else { 229 err = r.(error) 230 } 231 item = nil 232 } 233 }() 234 235 if in != nil && isTyped(reflect.TypeOf(in)) { 236 var out interface{} 237 in = convertToUntyped(in, out) 238 } 239 240 item = convertTo(in) 241 return item, nil 242 } 243 244 // ConvertFrom accepts a *dynamodb.AttributeValue and converts it to any interface{}. 245 // 246 // If v contains any structs, the result is first converted it to a interface{}, 247 // then JSON encoded/decoded it to convert to a struct, so `json` struct tags 248 // are respected. 249 // 250 // Deprecated: Use Unmarshal instead 251 func ConvertFrom(item *dynamodb.AttributeValue, v interface{}) (err error) { 252 defer func() { 253 if r := recover(); r != nil { 254 if e, ok := r.(runtime.Error); ok { 255 err = e 256 } else if s, ok := r.(string); ok { 257 err = fmt.Errorf(s) 258 } else { 259 err = r.(error) 260 } 261 item = nil 262 } 263 }() 264 265 rv := reflect.ValueOf(v) 266 if rv.Kind() != reflect.Ptr || rv.IsNil() { 267 return awserr.New("SerializationError", 268 fmt.Sprintf("v must be a non-nil pointer to an interface{} or struct, got %s", 269 rv.Type()), 270 nil) 271 } 272 if rv.Elem().Kind() != reflect.Interface && rv.Elem().Kind() != reflect.Struct { 273 return awserr.New("SerializationError", 274 fmt.Sprintf("v must be a non-nil pointer to an interface{} or struct, got %s", 275 rv.Type()), 276 nil) 277 } 278 279 res := convertFrom(item) 280 281 if isTyped(reflect.TypeOf(v)) { 282 err = convertToTyped(res, v) 283 } else if res != nil { 284 rv.Elem().Set(reflect.ValueOf(res)) 285 } 286 287 return err 288 } 289 290 func isTyped(v reflect.Type) bool { 291 switch v.Kind() { 292 case reflect.Struct: 293 return true 294 case reflect.Array, reflect.Slice: 295 if isTyped(v.Elem()) { 296 return true 297 } 298 case reflect.Map: 299 if isTyped(v.Key()) { 300 return true 301 } 302 if isTyped(v.Elem()) { 303 return true 304 } 305 case reflect.Ptr: 306 return isTyped(v.Elem()) 307 } 308 return false 309 } 310 311 func convertToUntyped(in, out interface{}) interface{} { 312 b, err := json.Marshal(in) 313 if err != nil { 314 panic(err) 315 } 316 317 decoder := json.NewDecoder(bytes.NewReader(b)) 318 decoder.UseNumber() 319 err = decoder.Decode(&out) 320 if err != nil { 321 panic(err) 322 } 323 324 return out 325 } 326 327 func convertToTyped(in, out interface{}) error { 328 b, err := json.Marshal(in) 329 if err != nil { 330 return err 331 } 332 333 decoder := json.NewDecoder(bytes.NewReader(b)) 334 return decoder.Decode(&out) 335 } 336 337 func convertTo(in interface{}) *dynamodb.AttributeValue { 338 a := &dynamodb.AttributeValue{} 339 340 if in == nil { 341 a.NULL = new(bool) 342 *a.NULL = true 343 return a 344 } 345 346 if m, ok := in.(map[string]interface{}); ok { 347 a.M = make(map[string]*dynamodb.AttributeValue) 348 for k, v := range m { 349 a.M[k] = convertTo(v) 350 } 351 return a 352 } 353 354 v := reflect.ValueOf(in) 355 switch v.Kind() { 356 case reflect.Bool: 357 a.BOOL = new(bool) 358 *a.BOOL = v.Bool() 359 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 360 a.N = new(string) 361 *a.N = strconv.FormatInt(v.Int(), 10) 362 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 363 a.N = new(string) 364 *a.N = strconv.FormatUint(v.Uint(), 10) 365 case reflect.Float32, reflect.Float64: 366 a.N = new(string) 367 *a.N = strconv.FormatFloat(v.Float(), 'f', -1, 64) 368 case reflect.String: 369 if n, ok := in.(json.Number); ok { 370 a.N = new(string) 371 *a.N = n.String() 372 } else { 373 a.S = new(string) 374 *a.S = v.String() 375 } 376 case reflect.Slice: 377 switch v.Type() { 378 case reflect.TypeOf(([]byte)(nil)): 379 a.B = v.Bytes() 380 default: 381 a.L = make([]*dynamodb.AttributeValue, v.Len()) 382 for i := 0; i < v.Len(); i++ { 383 a.L[i] = convertTo(v.Index(i).Interface()) 384 } 385 } 386 default: 387 panic(fmt.Sprintf("the type %s is not supported", v.Type().String())) 388 } 389 390 return a 391 } 392 393 func convertFrom(a *dynamodb.AttributeValue) interface{} { 394 if a.S != nil { 395 return *a.S 396 } 397 398 if a.N != nil { 399 // Number is tricky b/c we don't know which numeric type to use. Here we 400 // simply try the different types from most to least restrictive. 401 if n, err := strconv.ParseInt(*a.N, 10, 64); err == nil { 402 return int(n) 403 } 404 if n, err := strconv.ParseUint(*a.N, 10, 64); err == nil { 405 return uint(n) 406 } 407 n, err := strconv.ParseFloat(*a.N, 64) 408 if err != nil { 409 panic(err) 410 } 411 return n 412 } 413 414 if a.BOOL != nil { 415 return *a.BOOL 416 } 417 418 if a.NULL != nil { 419 return nil 420 } 421 422 if a.M != nil { 423 m := make(map[string]interface{}) 424 for k, v := range a.M { 425 m[k] = convertFrom(v) 426 } 427 return m 428 } 429 430 if a.L != nil { 431 l := make([]interface{}, len(a.L)) 432 for index, v := range a.L { 433 l[index] = convertFrom(v) 434 } 435 return l 436 } 437 438 if a.B != nil { 439 return a.B 440 } 441 442 panic(fmt.Sprintf("%#v is not a supported dynamodb.AttributeValue", a)) 443 }