@@ -442,22 +442,33 @@ func (d *Decoder) Decode(input interface{}) error {
442
442
return err
443
443
}
444
444
445
- // Decodes an unknown data type into a specific reflection value.
446
- func (d * Decoder ) decode (name string , input interface {}, outVal reflect.Value ) error {
447
- var inputVal reflect.Value
448
- if input != nil {
449
- inputVal = reflect .ValueOf (input )
450
-
451
- // We need to check here if input is a typed nil. Typed nils won't
452
- // match the "input == nil" below so we check that here.
453
- if inputVal .Kind () == reflect .Ptr && inputVal .IsNil () {
454
- input = nil
455
- }
445
+ // A comparison input == nil will fail if input is a typed nil.
446
+ // This function converts a typed nil to an actual, untyped nil.
447
+ func toRealNil (input interface {}) interface {} {
448
+ if input == nil {
449
+ return nil
456
450
}
451
+ val := reflect .ValueOf (input )
452
+ k := val .Kind ()
453
+ if (k == reflect .Ptr ||
454
+ k == reflect .Interface ||
455
+ k == reflect .Map ||
456
+ k == reflect .Slice ||
457
+ k == reflect .Array ) && val .IsNil () {
458
+ return nil
459
+ }
460
+ return input
461
+ }
457
462
458
- decodeNil := d .config .DecodeNil && d .config .DecodeHook != nil
459
-
460
- if input == nil {
463
+ // Decodes an unknown data type into a specific reflection value.
464
+ func (d * Decoder ) decode (name string , input interface {}, outVal reflect.Value ) error {
465
+ var (
466
+ inputVal = reflect .ValueOf (input )
467
+ outputKind = getKind (outVal )
468
+ decodeNil = d .config .DecodeNil && d .cachedDecodeHook != nil
469
+ )
470
+ input = toRealNil (input )
471
+ if input == nil || ! inputVal .IsValid () {
461
472
// If the data is nil, then we don't set anything, unless ZeroFields is set
462
473
// to true.
463
474
if d .config .ZeroFields {
@@ -467,40 +478,37 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
467
478
d .config .Metadata .Keys = append (d .config .Metadata .Keys , name )
468
479
}
469
480
}
470
-
471
481
if ! decodeNil {
472
482
return nil
473
483
}
474
484
}
475
485
476
- if ! inputVal .IsValid () {
477
- if ! decodeNil {
478
- // If the input value is invalid, then we just set the value
479
- // to be the zero value.
480
- outVal .Set (reflect .Zero (outVal .Type ()))
481
- if d .config .Metadata != nil && name != "" {
482
- d .config .Metadata .Keys = append (d .config .Metadata .Keys , name )
483
- }
484
- return nil
485
- }
486
-
487
- // If we get here, we have an untyped nil so the type of the input is assumed.
488
- // We do this because all subsequent code requires a valid value for inputVal.
489
- var mapVal map [string ]interface {}
490
- inputVal = reflect .MakeMap (reflect .TypeOf (mapVal ))
491
- }
492
-
493
486
if d .cachedDecodeHook != nil {
494
487
// We have a DecodeHook, so let's pre-process the input.
488
+ if ! inputVal .IsValid () {
489
+ // Hooks need a valid inputVal, so reset it to zero value of outVal type.
490
+ switch outputKind {
491
+ case reflect .Struct , reflect .Map :
492
+ var mapVal map [string ]interface {}
493
+ inputVal = reflect .ValueOf (mapVal )
494
+ case reflect .Slice , reflect .Array :
495
+ var sliceVal []interface {}
496
+ inputVal = reflect .ValueOf (sliceVal )
497
+ default :
498
+ inputVal = reflect .Zero (outVal .Type ())
499
+ }
500
+ }
495
501
var err error
496
502
input , err = d .cachedDecodeHook (inputVal , outVal )
497
503
if err != nil {
498
504
return fmt .Errorf ("error decoding '%s': %w" , name , err )
499
505
}
500
506
}
507
+ if toRealNil (input ) == nil {
508
+ return nil
509
+ }
501
510
502
511
var err error
503
- outputKind := getKind (outVal )
504
512
addMetaKey := true
505
513
switch outputKind {
506
514
case reflect .Bool :
0 commit comments