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 av.B != nil: 178 return d.decodeBinary(av.B, v) 179 case av.BOOL != nil: 180 return d.decodeBool(av.BOOL, v) 181 case av.BS != nil: 182 return d.decodeBinarySet(av.BS, v) 183 case av.L != nil: 184 return d.decodeList(av.L, v) 185 case av.M != nil: 186 return d.decodeMap(av.M, v) 187 case av.N != nil: 188 return d.decodeNumber(av.N, v, fieldTag) 189 case av.NS != nil: 190 return d.decodeNumberSet(av.NS, v) 191 case av.S != nil: 192 return d.decodeString(av.S, v, fieldTag) 193 case av.SS != nil: 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.SetString(*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 := fields.FieldByName(k); ok { 504 fv := decoderFieldByIndex(v, f.Index) 505 if err := d.decode(av, fv, f.tag); err != nil { 506 return err 507 } 508 } 509 } 510 } 511 512 return nil 513} 514 515func (d *Decoder) decodeNull(v reflect.Value) error { 516 if v.IsValid() && v.CanSet() { 517 v.Set(reflect.Zero(v.Type())) 518 } 519 520 return nil 521} 522 523func (d *Decoder) decodeString(s *string, v reflect.Value, fieldTag tag) error { 524 if fieldTag.AsString { 525 return d.decodeNumber(s, v, fieldTag) 526 } 527 528 // To maintain backwards compatibility with ConvertFrom family of methods which 529 // converted strings to time.Time structs 530 if v.Type().ConvertibleTo(timeType) { 531 t, err := time.Parse(time.RFC3339, *s) 532 if err != nil { 533 return err 534 } 535 v.Set(reflect.ValueOf(t).Convert(v.Type())) 536 return nil 537 } 538 539 switch v.Kind() { 540 case reflect.String: 541 v.SetString(*s) 542 case reflect.Slice: 543 // To maintain backwards compatibility with the ConvertFrom family of methods 544 // which converted []byte into base64-encoded strings if the input was typed 545 if v.Type() == byteSliceType { 546 decoded, err := base64.StdEncoding.DecodeString(*s) 547 if err != nil { 548 return &UnmarshalError{Err: err, Value: "string", Type: v.Type()} 549 } 550 v.SetBytes(decoded) 551 } 552 case reflect.Interface: 553 // Ensure type aliasing is handled properly 554 v.Set(reflect.ValueOf(*s).Convert(v.Type())) 555 default: 556 return &UnmarshalTypeError{Value: "string", Type: v.Type()} 557 } 558 559 return nil 560} 561 562func (d *Decoder) decodeStringSet(ss []*string, v reflect.Value) error { 563 isArray := false 564 565 switch v.Kind() { 566 case reflect.Slice: 567 // Make room for the slice elements if needed 568 if v.IsNil() || v.Cap() < len(ss) { 569 v.Set(reflect.MakeSlice(v.Type(), 0, len(ss))) 570 } 571 case reflect.Array: 572 // Limited to capacity of existing array. 573 isArray = true 574 case reflect.Interface: 575 set := make([]string, len(ss)) 576 for i, s := range ss { 577 if err := d.decodeString(s, reflect.ValueOf(&set[i]).Elem(), tag{}); err != nil { 578 return err 579 } 580 } 581 v.Set(reflect.ValueOf(set)) 582 return nil 583 default: 584 return &UnmarshalTypeError{Value: "string set", Type: v.Type()} 585 } 586 587 for i := 0; i < v.Cap() && i < len(ss); i++ { 588 if !isArray { 589 v.SetLen(i + 1) 590 } 591 u, elem := indirect(v.Index(i), false) 592 if u != nil { 593 return u.UnmarshalDynamoDBAttributeValue(&dynamodb.AttributeValue{SS: ss}) 594 } 595 if err := d.decodeString(ss[i], elem, tag{}); err != nil { 596 return err 597 } 598 } 599 600 return nil 601} 602 603func decodeUnixTime(n string) (time.Time, error) { 604 v, err := strconv.ParseInt(n, 10, 64) 605 if err != nil { 606 return time.Time{}, &UnmarshalError{ 607 Err: err, Value: n, Type: timeType, 608 } 609 } 610 611 return time.Unix(v, 0), nil 612} 613 614// decoderFieldByIndex finds the field with the provided nested index, allocating 615// embedded parent structs if needed 616func decoderFieldByIndex(v reflect.Value, index []int) reflect.Value { 617 for i, x := range index { 618 if i > 0 && v.Kind() == reflect.Ptr && v.Type().Elem().Kind() == reflect.Struct { 619 if v.IsNil() { 620 v.Set(reflect.New(v.Type().Elem())) 621 } 622 v = v.Elem() 623 } 624 v = v.Field(x) 625 } 626 return v 627} 628 629// indirect will walk a value's interface or pointer value types. Returning 630// the final value or the value a unmarshaler is defined on. 631// 632// Based on the enoding/json type reflect value type indirection in Go Stdlib 633// https://golang.org/src/encoding/json/decode.go indirect func. 634func indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) { 635 if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { 636 v = v.Addr() 637 } 638 for { 639 if v.Kind() == reflect.Interface && !v.IsNil() { 640 e := v.Elem() 641 if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { 642 v = e 643 continue 644 } 645 } 646 if v.Kind() != reflect.Ptr { 647 break 648 } 649 if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { 650 break 651 } 652 if v.IsNil() { 653 v.Set(reflect.New(v.Type().Elem())) 654 } 655 if v.Type().NumMethod() > 0 { 656 if u, ok := v.Interface().(Unmarshaler); ok { 657 return u, reflect.Value{} 658 } 659 } 660 v = v.Elem() 661 } 662 663 return nil, v 664} 665 666// A Number represents a Attributevalue number literal. 667type Number string 668 669// Float64 attempts to cast the number ot a float64, returning 670// the result of the case or error if the case failed. 671func (n Number) Float64() (float64, error) { 672 return strconv.ParseFloat(string(n), 64) 673} 674 675// Int64 attempts to cast the number ot a int64, returning 676// the result of the case or error if the case failed. 677func (n Number) Int64() (int64, error) { 678 return strconv.ParseInt(string(n), 10, 64) 679} 680 681// Uint64 attempts to cast the number ot a uint64, returning 682// the result of the case or error if the case failed. 683func (n Number) Uint64() (uint64, error) { 684 return strconv.ParseUint(string(n), 10, 64) 685} 686 687// String returns the raw number represented as a string 688func (n Number) String() string { 689 return string(n) 690} 691 692type emptyOrigError struct{} 693 694func (e emptyOrigError) OrigErr() error { 695 return nil 696} 697 698// An UnmarshalTypeError is an error type representing a error 699// unmarshaling the AttributeValue's element to a Go value type. 700// Includes details about the AttributeValue type and Go value type. 701type UnmarshalTypeError struct { 702 emptyOrigError 703 Value string 704 Type reflect.Type 705} 706 707// Error returns the string representation of the error. 708// satisfying the error interface 709func (e *UnmarshalTypeError) Error() string { 710 return fmt.Sprintf("%s: %s", e.Code(), e.Message()) 711} 712 713// Code returns the code of the error, satisfying the awserr.Error 714// interface. 715func (e *UnmarshalTypeError) Code() string { 716 return "UnmarshalTypeError" 717} 718 719// Message returns the detailed message of the error, satisfying 720// the awserr.Error interface. 721func (e *UnmarshalTypeError) Message() string { 722 return "cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() 723} 724 725// An InvalidUnmarshalError is an error type representing an invalid type 726// encountered while unmarshaling a AttributeValue to a Go value type. 727type InvalidUnmarshalError struct { 728 emptyOrigError 729 Type reflect.Type 730} 731 732// Error returns the string representation of the error. 733// satisfying the error interface 734func (e *InvalidUnmarshalError) Error() string { 735 return fmt.Sprintf("%s: %s", e.Code(), e.Message()) 736} 737 738// Code returns the code of the error, satisfying the awserr.Error 739// interface. 740func (e *InvalidUnmarshalError) Code() string { 741 return "InvalidUnmarshalError" 742} 743 744// Message returns the detailed message of the error, satisfying 745// the awserr.Error interface. 746func (e *InvalidUnmarshalError) Message() string { 747 if e.Type == nil { 748 return "cannot unmarshal to nil value" 749 } 750 if e.Type.Kind() != reflect.Ptr { 751 return "cannot unmarshal to non-pointer value, got " + e.Type.String() 752 } 753 return "cannot unmarshal to nil value, " + e.Type.String() 754} 755 756// An UnmarshalError wraps an error that occurred while unmarshaling a DynamoDB 757// AttributeValue element into a Go type. This is different from UnmarshalTypeError 758// in that it wraps the underlying error that occurred. 759type UnmarshalError struct { 760 Err error 761 Value string 762 Type reflect.Type 763} 764 765// Error returns the string representation of the error. 766// satisfying the error interface. 767func (e *UnmarshalError) Error() string { 768 return fmt.Sprintf("%s: %s\ncaused by: %v", e.Code(), e.Message(), e.Err) 769} 770 771// OrigErr returns the original error that caused this issue. 772func (e UnmarshalError) OrigErr() error { 773 return e.Err 774} 775 776// Code returns the code of the error, satisfying the awserr.Error 777// interface. 778func (e *UnmarshalError) Code() string { 779 return "UnmarshalError" 780} 781 782// Message returns the detailed message of the error, satisfying 783// the awserr.Error interface. 784func (e *UnmarshalError) Message() string { 785 return fmt.Sprintf("cannot unmarshal %q into %s.", 786 e.Value, e.Type.String()) 787} 788