1package dynamodbattribute 2 3import ( 4 "encoding/base64" 5 "fmt" 6 "reflect" 7 "strconv" 8 "time" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/service/dynamodb" 12) 13 14// An Unmarshaler is an interface to provide custom unmarshaling of 15// AttributeValues. Use this to provide custom logic determining 16// how AttributeValues should be unmarshaled. 17// type ExampleUnmarshaler struct { 18// Value int 19// } 20// 21// func (u *ExampleUnmarshaler) UnmarshalDynamoDBAttributeValue(av *dynamodb.AttributeValue) error { 22// if av.N == nil { 23// return nil 24// } 25// 26// n, err := strconv.ParseInt(*av.N, 10, 0) 27// if err != nil { 28// return err 29// } 30// 31// u.Value = int(n) 32// return nil 33// } 34type Unmarshaler interface { 35 UnmarshalDynamoDBAttributeValue(*dynamodb.AttributeValue) error 36} 37 38// Unmarshal will unmarshal DynamoDB AttributeValues to Go value types. 39// Both generic interface{} and concrete types are valid unmarshal 40// destination types. 41// 42// Unmarshal will allocate maps, slices, and pointers as needed to 43// unmarshal the AttributeValue into the provided type value. 44// 45// When unmarshaling AttributeValues into structs Unmarshal matches 46// the field names of the struct to the AttributeValue Map keys. 47// Initially it will look for exact field name matching, but will 48// fall back to case insensitive if not exact match is found. 49// 50// With the exception of omitempty, omitemptyelem, binaryset, numberset 51// and stringset all struct tags used by Marshal are also used by 52// Unmarshal. 53// 54// When decoding AttributeValues to interfaces Unmarshal will use the 55// following types. 56// 57// []byte, AV Binary (B) 58// [][]byte, AV Binary Set (BS) 59// bool, AV Boolean (BOOL) 60// []interface{}, AV List (L) 61// map[string]interface{}, AV Map (M) 62// float64, AV Number (N) 63// Number, AV Number (N) with UseNumber set 64// []float64, AV Number Set (NS) 65// []Number, AV Number Set (NS) with UseNumber set 66// string, AV String (S) 67// []string, AV String Set (SS) 68// 69// If the Decoder option, UseNumber is set numbers will be unmarshaled 70// as Number values instead of float64. Use this to maintain the original 71// string formating of the number as it was represented in the AttributeValue. 72// In addition provides additional opportunities to parse the number 73// string based on individual use cases. 74// 75// When unmarshaling any error that occurs will halt the unmarshal 76// and return the error. 77// 78// The output value provided must be a non-nil pointer 79func Unmarshal(av *dynamodb.AttributeValue, out interface{}) error { 80 return NewDecoder().Decode(av, out) 81} 82 83// UnmarshalMap is an alias for Unmarshal which unmarshals from 84// a map of AttributeValues. 85// 86// The output value provided must be a non-nil pointer 87func UnmarshalMap(m map[string]*dynamodb.AttributeValue, out interface{}) error { 88 return NewDecoder().Decode(&dynamodb.AttributeValue{M: m}, out) 89} 90 91// UnmarshalList is an alias for Unmarshal func which unmarshals 92// a slice of AttributeValues. 93// 94// The output value provided must be a non-nil pointer 95func UnmarshalList(l []*dynamodb.AttributeValue, out interface{}) error { 96 return NewDecoder().Decode(&dynamodb.AttributeValue{L: l}, out) 97} 98 99// UnmarshalListOfMaps is an alias for Unmarshal func which unmarshals a 100// slice of maps of attribute values. 101// 102// This is useful for when you need to unmarshal the Items from a DynamoDB 103// Query API call. 104// 105// The output value provided must be a non-nil pointer 106func UnmarshalListOfMaps(l []map[string]*dynamodb.AttributeValue, out interface{}) error { 107 items := make([]*dynamodb.AttributeValue, len(l)) 108 for i, m := range l { 109 items[i] = &dynamodb.AttributeValue{M: m} 110 } 111 112 return UnmarshalList(items, out) 113} 114 115// A Decoder provides unmarshaling AttributeValues to Go value types. 116type Decoder struct { 117 MarshalOptions 118 119 // Instructs the decoder to decode AttributeValue Numbers as 120 // Number type instead of float64 when the destination type 121 // is interface{}. Similar to encoding/json.Number 122 UseNumber bool 123} 124 125// NewDecoder creates a new Decoder with default configuration. Use 126// the `opts` functional options to override the default configuration. 127func NewDecoder(opts ...func(*Decoder)) *Decoder { 128 d := &Decoder{ 129 MarshalOptions: MarshalOptions{ 130 SupportJSONTags: true, 131 }, 132 } 133 for _, o := range opts { 134 o(d) 135 } 136 137 return d 138} 139 140// Decode will unmarshal an AttributeValue into a Go value type. An error 141// will be return if the decoder is unable to unmarshal the AttributeValue 142// to the provide Go value type. 143// 144// The output value provided must be a non-nil pointer 145func (d *Decoder) Decode(av *dynamodb.AttributeValue, out interface{}, opts ...func(*Decoder)) error { 146 v := reflect.ValueOf(out) 147 if v.Kind() != reflect.Ptr || v.IsNil() || !v.IsValid() { 148 return &InvalidUnmarshalError{Type: reflect.TypeOf(out)} 149 } 150 151 return d.decode(av, v, tag{}) 152} 153 154var stringInterfaceMapType = reflect.TypeOf(map[string]interface{}(nil)) 155var byteSliceType = reflect.TypeOf([]byte(nil)) 156var byteSliceSlicetype = reflect.TypeOf([][]byte(nil)) 157var numberType = reflect.TypeOf(Number("")) 158var timeType = reflect.TypeOf(time.Time{}) 159var ptrStringType = reflect.TypeOf(aws.String("")) 160 161func (d *Decoder) decode(av *dynamodb.AttributeValue, v reflect.Value, fieldTag tag) error { 162 var u Unmarshaler 163 if av == nil || av.NULL != nil { 164 u, v = indirect(v, true) 165 if u != nil { 166 return u.UnmarshalDynamoDBAttributeValue(av) 167 } 168 return d.decodeNull(v) 169 } 170 171 u, v = indirect(v, false) 172 if u != nil { 173 return u.UnmarshalDynamoDBAttributeValue(av) 174 } 175 176 switch { 177 case len(av.B) != 0 || (av.B != nil && d.EnableEmptyCollections): 178 return d.decodeBinary(av.B, v) 179 case av.BOOL != nil: 180 return d.decodeBool(av.BOOL, v) 181 case len(av.BS) != 0 || (av.BS != nil && d.EnableEmptyCollections): 182 return d.decodeBinarySet(av.BS, v) 183 case len(av.L) != 0 || (av.L != nil && d.EnableEmptyCollections): 184 return d.decodeList(av.L, v) 185 case len(av.M) != 0 || (av.M != nil && d.EnableEmptyCollections): 186 return d.decodeMap(av.M, v) 187 case av.N != nil: 188 return d.decodeNumber(av.N, v, fieldTag) 189 case len(av.NS) != 0 || (av.NS != nil && d.EnableEmptyCollections): 190 return d.decodeNumberSet(av.NS, v) 191 case av.S != nil: // DynamoDB does not allow for empty strings, so we do not consider the length or EnableEmptyCollections flag here 192 return d.decodeString(av.S, v, fieldTag) 193 case len(av.SS) != 0 || (av.SS != nil && d.EnableEmptyCollections): 194 return d.decodeStringSet(av.SS, v) 195 } 196 197 return nil 198} 199 200func (d *Decoder) decodeBinary(b []byte, v reflect.Value) error { 201 if v.Kind() == reflect.Interface { 202 buf := make([]byte, len(b)) 203 copy(buf, b) 204 v.Set(reflect.ValueOf(buf)) 205 return nil 206 } 207 208 if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { 209 return &UnmarshalTypeError{Value: "binary", Type: v.Type()} 210 } 211 212 if v.Type() == byteSliceType { 213 // Optimization for []byte types 214 if v.IsNil() || v.Cap() < len(b) { 215 v.Set(reflect.MakeSlice(byteSliceType, len(b), len(b))) 216 } else if v.Len() != len(b) { 217 v.SetLen(len(b)) 218 } 219 copy(v.Interface().([]byte), b) 220 return nil 221 } 222 223 switch v.Type().Elem().Kind() { 224 case reflect.Uint8: 225 // Fallback to reflection copy for type aliased of []byte type 226 if v.Kind() != reflect.Array && (v.IsNil() || v.Cap() < len(b)) { 227 v.Set(reflect.MakeSlice(v.Type(), len(b), len(b))) 228 } else if v.Len() != len(b) { 229 v.SetLen(len(b)) 230 } 231 for i := 0; i < len(b); i++ { 232 v.Index(i).SetUint(uint64(b[i])) 233 } 234 default: 235 if v.Kind() == reflect.Array { 236 switch v.Type().Elem().Kind() { 237 case reflect.Uint8: 238 reflect.Copy(v, reflect.ValueOf(b)) 239 default: 240 return &UnmarshalTypeError{Value: "binary", Type: v.Type()} 241 } 242 243 break 244 } 245 246 return &UnmarshalTypeError{Value: "binary", Type: v.Type()} 247 } 248 249 return nil 250} 251 252func (d *Decoder) decodeBool(b *bool, v reflect.Value) error { 253 switch v.Kind() { 254 case reflect.Bool, reflect.Interface: 255 v.Set(reflect.ValueOf(*b).Convert(v.Type())) 256 default: 257 return &UnmarshalTypeError{Value: "bool", Type: v.Type()} 258 } 259 260 return nil 261} 262 263func (d *Decoder) decodeBinarySet(bs [][]byte, v reflect.Value) error { 264 isArray := false 265 266 switch v.Kind() { 267 case reflect.Slice: 268 // Make room for the slice elements if needed 269 if v.IsNil() || v.Cap() < len(bs) { 270 // What about if ignoring nil/empty values? 271 v.Set(reflect.MakeSlice(v.Type(), 0, len(bs))) 272 } 273 case reflect.Array: 274 // Limited to capacity of existing array. 275 isArray = true 276 case reflect.Interface: 277 set := make([][]byte, len(bs)) 278 for i, b := range bs { 279 if err := d.decodeBinary(b, reflect.ValueOf(&set[i]).Elem()); err != nil { 280 return err 281 } 282 } 283 v.Set(reflect.ValueOf(set)) 284 return nil 285 default: 286 return &UnmarshalTypeError{Value: "binary set", Type: v.Type()} 287 } 288 289 for i := 0; i < v.Cap() && i < len(bs); i++ { 290 if !isArray { 291 v.SetLen(i + 1) 292 } 293 u, elem := indirect(v.Index(i), false) 294 if u != nil { 295 return u.UnmarshalDynamoDBAttributeValue(&dynamodb.AttributeValue{BS: bs}) 296 } 297 if err := d.decodeBinary(bs[i], elem); err != nil { 298 return err 299 } 300 } 301 302 return nil 303} 304 305func (d *Decoder) decodeNumber(n *string, v reflect.Value, fieldTag tag) error { 306 switch v.Kind() { 307 case reflect.Interface: 308 i, err := d.decodeNumberToInterface(n) 309 if err != nil { 310 return err 311 } 312 v.Set(reflect.ValueOf(i)) 313 return nil 314 case reflect.String: 315 if v.Type() == numberType { // Support Number value type 316 v.Set(reflect.ValueOf(Number(*n))) 317 return nil 318 } 319 v.Set(reflect.ValueOf(*n)) 320 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 321 i, err := strconv.ParseInt(*n, 10, 64) 322 if err != nil { 323 return err 324 } 325 if v.OverflowInt(i) { 326 return &UnmarshalTypeError{ 327 Value: fmt.Sprintf("number overflow, %s", *n), 328 Type: v.Type(), 329 } 330 } 331 v.SetInt(i) 332 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 333 i, err := strconv.ParseUint(*n, 10, 64) 334 if err != nil { 335 return err 336 } 337 if v.OverflowUint(i) { 338 return &UnmarshalTypeError{ 339 Value: fmt.Sprintf("number overflow, %s", *n), 340 Type: v.Type(), 341 } 342 } 343 v.SetUint(i) 344 case reflect.Float32, reflect.Float64: 345 i, err := strconv.ParseFloat(*n, 64) 346 if err != nil { 347 return err 348 } 349 if v.OverflowFloat(i) { 350 return &UnmarshalTypeError{ 351 Value: fmt.Sprintf("number overflow, %s", *n), 352 Type: v.Type(), 353 } 354 } 355 v.SetFloat(i) 356 default: 357 if v.Type().ConvertibleTo(timeType) && fieldTag.AsUnixTime { 358 t, err := decodeUnixTime(*n) 359 if err != nil { 360 return err 361 } 362 v.Set(reflect.ValueOf(t).Convert(v.Type())) 363 return nil 364 } 365 return &UnmarshalTypeError{Value: "number", Type: v.Type()} 366 } 367 368 return nil 369} 370 371func (d *Decoder) decodeNumberToInterface(n *string) (interface{}, error) { 372 if d.UseNumber { 373 return Number(*n), nil 374 } 375 376 // Default to float64 for all numbers 377 return strconv.ParseFloat(*n, 64) 378} 379 380func (d *Decoder) decodeNumberSet(ns []*string, v reflect.Value) error { 381 isArray := false 382 383 switch v.Kind() { 384 case reflect.Slice: 385 // Make room for the slice elements if needed 386 if v.IsNil() || v.Cap() < len(ns) { 387 // What about if ignoring nil/empty values? 388 v.Set(reflect.MakeSlice(v.Type(), 0, len(ns))) 389 } 390 case reflect.Array: 391 // Limited to capacity of existing array. 392 isArray = true 393 case reflect.Interface: 394 if d.UseNumber { 395 set := make([]Number, len(ns)) 396 for i, n := range ns { 397 if err := d.decodeNumber(n, reflect.ValueOf(&set[i]).Elem(), tag{}); err != nil { 398 return err 399 } 400 } 401 v.Set(reflect.ValueOf(set)) 402 } else { 403 set := make([]float64, len(ns)) 404 for i, n := range ns { 405 if err := d.decodeNumber(n, reflect.ValueOf(&set[i]).Elem(), tag{}); err != nil { 406 return err 407 } 408 } 409 v.Set(reflect.ValueOf(set)) 410 } 411 return nil 412 default: 413 return &UnmarshalTypeError{Value: "number set", Type: v.Type()} 414 } 415 416 for i := 0; i < v.Cap() && i < len(ns); i++ { 417 if !isArray { 418 v.SetLen(i + 1) 419 } 420 u, elem := indirect(v.Index(i), false) 421 if u != nil { 422 return u.UnmarshalDynamoDBAttributeValue(&dynamodb.AttributeValue{NS: ns}) 423 } 424 if err := d.decodeNumber(ns[i], elem, tag{}); err != nil { 425 return err 426 } 427 } 428 429 return nil 430} 431 432func (d *Decoder) decodeList(avList []*dynamodb.AttributeValue, v reflect.Value) error { 433 isArray := false 434 435 switch v.Kind() { 436 case reflect.Slice: 437 // Make room for the slice elements if needed 438 if v.IsNil() || v.Cap() < len(avList) { 439 // What about if ignoring nil/empty values? 440 v.Set(reflect.MakeSlice(v.Type(), 0, len(avList))) 441 } 442 case reflect.Array: 443 // Limited to capacity of existing array. 444 isArray = true 445 case reflect.Interface: 446 s := make([]interface{}, len(avList)) 447 for i, av := range avList { 448 if err := d.decode(av, reflect.ValueOf(&s[i]).Elem(), tag{}); err != nil { 449 return err 450 } 451 } 452 v.Set(reflect.ValueOf(s)) 453 return nil 454 default: 455 return &UnmarshalTypeError{Value: "list", Type: v.Type()} 456 } 457 458 // If v is not a slice, array 459 for i := 0; i < v.Cap() && i < len(avList); i++ { 460 if !isArray { 461 v.SetLen(i + 1) 462 } 463 464 if err := d.decode(avList[i], v.Index(i), tag{}); err != nil { 465 return err 466 } 467 } 468 469 return nil 470} 471 472func (d *Decoder) decodeMap(avMap map[string]*dynamodb.AttributeValue, v reflect.Value) error { 473 switch v.Kind() { 474 case reflect.Map: 475 t := v.Type() 476 if t.Key().Kind() != reflect.String { 477 return &UnmarshalTypeError{Value: "map string key", Type: t.Key()} 478 } 479 if v.IsNil() { 480 v.Set(reflect.MakeMap(t)) 481 } 482 case reflect.Struct: 483 case reflect.Interface: 484 v.Set(reflect.MakeMap(stringInterfaceMapType)) 485 v = v.Elem() 486 default: 487 return &UnmarshalTypeError{Value: "map", Type: v.Type()} 488 } 489 490 if v.Kind() == reflect.Map { 491 for k, av := range avMap { 492 key := reflect.New(v.Type().Key()).Elem() 493 key.SetString(k) 494 elem := reflect.New(v.Type().Elem()).Elem() 495 if err := d.decode(av, elem, tag{}); err != nil { 496 return err 497 } 498 v.SetMapIndex(key, elem) 499 } 500 } else if v.Kind() == reflect.Struct { 501 fields := unionStructFields(v.Type(), d.MarshalOptions) 502 for k, av := range avMap { 503 if f, ok := fieldByName(fields, k); ok { 504 fv := fieldByIndex(v, f.Index, func(v *reflect.Value) bool { 505 v.Set(reflect.New(v.Type().Elem())) 506 return true // to continue the loop. 507 }) 508 if err := d.decode(av, fv, f.tag); err != nil { 509 return err 510 } 511 } 512 } 513 } 514 515 return nil 516} 517 518func (d *Decoder) decodeNull(v reflect.Value) error { 519 if v.IsValid() && v.CanSet() { 520 v.Set(reflect.Zero(v.Type())) 521 } 522 523 return nil 524} 525 526func (d *Decoder) decodeString(s *string, v reflect.Value, fieldTag tag) error { 527 if fieldTag.AsString { 528 return d.decodeNumber(s, v, fieldTag) 529 } 530 531 // To maintain backwards compatibility with ConvertFrom family of methods which 532 // converted strings to time.Time structs 533 if v.Type().ConvertibleTo(timeType) { 534 t, err := time.Parse(time.RFC3339, *s) 535 if err != nil { 536 return err 537 } 538 v.Set(reflect.ValueOf(t).Convert(v.Type())) 539 return nil 540 } 541 542 switch v.Kind() { 543 case reflect.String: 544 v.SetString(*s) 545 case reflect.Slice: 546 // To maintain backwards compatibility with the ConvertFrom family of methods 547 // which converted []byte into base64-encoded strings if the input was typed 548 if v.Type() == byteSliceType { 549 decoded, err := base64.StdEncoding.DecodeString(*s) 550 if err != nil { 551 return &UnmarshalError{Err: err, Value: "string", Type: v.Type()} 552 } 553 v.SetBytes(decoded) 554 } 555 case reflect.Interface: 556 // Ensure type aliasing is handled properly 557 v.Set(reflect.ValueOf(*s).Convert(v.Type())) 558 default: 559 return &UnmarshalTypeError{Value: "string", Type: v.Type()} 560 } 561 562 return nil 563} 564 565func (d *Decoder) decodeStringSet(ss []*string, v reflect.Value) error { 566 isArray := false 567 568 switch v.Kind() { 569 case reflect.Slice: 570 // Make room for the slice elements if needed 571 if v.IsNil() || v.Cap() < len(ss) { 572 v.Set(reflect.MakeSlice(v.Type(), 0, len(ss))) 573 } 574 case reflect.Array: 575 // Limited to capacity of existing array. 576 isArray = true 577 case reflect.Interface: 578 set := make([]string, len(ss)) 579 for i, s := range ss { 580 if err := d.decodeString(s, reflect.ValueOf(&set[i]).Elem(), tag{}); err != nil { 581 return err 582 } 583 } 584 v.Set(reflect.ValueOf(set)) 585 return nil 586 default: 587 return &UnmarshalTypeError{Value: "string set", Type: v.Type()} 588 } 589 590 for i := 0; i < v.Cap() && i < len(ss); i++ { 591 if !isArray { 592 v.SetLen(i + 1) 593 } 594 u, elem := indirect(v.Index(i), false) 595 if u != nil { 596 return u.UnmarshalDynamoDBAttributeValue(&dynamodb.AttributeValue{SS: ss}) 597 } 598 if err := d.decodeString(ss[i], elem, tag{}); err != nil { 599 return err 600 } 601 } 602 603 return nil 604} 605 606func decodeUnixTime(n string) (time.Time, error) { 607 v, err := strconv.ParseInt(n, 10, 64) 608 if err != nil { 609 return time.Time{}, &UnmarshalError{ 610 Err: err, Value: n, Type: timeType, 611 } 612 } 613 614 return time.Unix(v, 0), nil 615} 616 617// indirect will walk a value's interface or pointer value types. Returning 618// the final value or the value a unmarshaler is defined on. 619// 620// Based on the enoding/json type reflect value type indirection in Go Stdlib 621// https://golang.org/src/encoding/json/decode.go indirect func. 622func indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) { 623 if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { 624 v = v.Addr() 625 } 626 for { 627 if v.Kind() == reflect.Interface && !v.IsNil() { 628 e := v.Elem() 629 if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { 630 v = e 631 continue 632 } 633 } 634 if v.Kind() != reflect.Ptr { 635 break 636 } 637 if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { 638 break 639 } 640 if v.IsNil() { 641 v.Set(reflect.New(v.Type().Elem())) 642 } 643 if v.Type().NumMethod() > 0 { 644 if u, ok := v.Interface().(Unmarshaler); ok { 645 return u, reflect.Value{} 646 } 647 } 648 v = v.Elem() 649 } 650 651 return nil, v 652} 653 654// A Number represents a Attributevalue number literal. 655type Number string 656 657// Float64 attempts to cast the number ot a float64, returning 658// the result of the case or error if the case failed. 659func (n Number) Float64() (float64, error) { 660 return strconv.ParseFloat(string(n), 64) 661} 662 663// Int64 attempts to cast the number ot a int64, returning 664// the result of the case or error if the case failed. 665func (n Number) Int64() (int64, error) { 666 return strconv.ParseInt(string(n), 10, 64) 667} 668 669// Uint64 attempts to cast the number ot a uint64, returning 670// the result of the case or error if the case failed. 671func (n Number) Uint64() (uint64, error) { 672 return strconv.ParseUint(string(n), 10, 64) 673} 674 675// String returns the raw number represented as a string 676func (n Number) String() string { 677 return string(n) 678} 679 680type emptyOrigError struct{} 681 682func (e emptyOrigError) OrigErr() error { 683 return nil 684} 685 686// An UnmarshalTypeError is an error type representing a error 687// unmarshaling the AttributeValue's element to a Go value type. 688// Includes details about the AttributeValue type and Go value type. 689type UnmarshalTypeError struct { 690 emptyOrigError 691 Value string 692 Type reflect.Type 693} 694 695// Error returns the string representation of the error. 696// satisfying the error interface 697func (e *UnmarshalTypeError) Error() string { 698 return fmt.Sprintf("%s: %s", e.Code(), e.Message()) 699} 700 701// Code returns the code of the error, satisfying the awserr.Error 702// interface. 703func (e *UnmarshalTypeError) Code() string { 704 return "UnmarshalTypeError" 705} 706 707// Message returns the detailed message of the error, satisfying 708// the awserr.Error interface. 709func (e *UnmarshalTypeError) Message() string { 710 return "cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() 711} 712 713// An InvalidUnmarshalError is an error type representing an invalid type 714// encountered while unmarshaling a AttributeValue to a Go value type. 715type InvalidUnmarshalError struct { 716 emptyOrigError 717 Type reflect.Type 718} 719 720// Error returns the string representation of the error. 721// satisfying the error interface 722func (e *InvalidUnmarshalError) Error() string { 723 return fmt.Sprintf("%s: %s", e.Code(), e.Message()) 724} 725 726// Code returns the code of the error, satisfying the awserr.Error 727// interface. 728func (e *InvalidUnmarshalError) Code() string { 729 return "InvalidUnmarshalError" 730} 731 732// Message returns the detailed message of the error, satisfying 733// the awserr.Error interface. 734func (e *InvalidUnmarshalError) Message() string { 735 if e.Type == nil { 736 return "cannot unmarshal to nil value" 737 } 738 if e.Type.Kind() != reflect.Ptr { 739 return "cannot unmarshal to non-pointer value, got " + e.Type.String() 740 } 741 return "cannot unmarshal to nil value, " + e.Type.String() 742} 743 744// An UnmarshalError wraps an error that occurred while unmarshaling a DynamoDB 745// AttributeValue element into a Go type. This is different from UnmarshalTypeError 746// in that it wraps the underlying error that occurred. 747type UnmarshalError struct { 748 Err error 749 Value string 750 Type reflect.Type 751} 752 753// Error returns the string representation of the error. 754// satisfying the error interface. 755func (e *UnmarshalError) Error() string { 756 return fmt.Sprintf("%s: %s\ncaused by: %v", e.Code(), e.Message(), e.Err) 757} 758 759// OrigErr returns the original error that caused this issue. 760func (e UnmarshalError) OrigErr() error { 761 return e.Err 762} 763 764// Code returns the code of the error, satisfying the awserr.Error 765// interface. 766func (e *UnmarshalError) Code() string { 767 return "UnmarshalError" 768} 769 770// Message returns the detailed message of the error, satisfying 771// the awserr.Error interface. 772func (e *UnmarshalError) Message() string { 773 return fmt.Sprintf("cannot unmarshal %q into %s.", 774 e.Value, e.Type.String()) 775} 776