Skip to content

Commit

Permalink
Pass appropriate empty Value to hooks
Browse files Browse the repository at this point in the history
Signed-off-by: Yuri Shkuro <[email protected]>
  • Loading branch information
yurishkuro committed Sep 21, 2024
1 parent abbd7b4 commit cd1c879
Showing 1 changed file with 41 additions and 33 deletions.
74 changes: 41 additions & 33 deletions mapstructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,22 +442,33 @@ func (d *Decoder) Decode(input interface{}) error {
return err
}

// Decodes an unknown data type into a specific reflection value.
func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error {
var inputVal reflect.Value
if input != nil {
inputVal = reflect.ValueOf(input)

// We need to check here if input is a typed nil. Typed nils won't
// match the "input == nil" below so we check that here.
if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() {
input = nil
}
// A comparison input == nil will fail if input is a typed nil.
// This function converts a typed nil to an actual, untyped nil.
func toRealNil(input interface{}) interface{} {
if input == nil {
return nil
}
val := reflect.ValueOf(input)
k := val.Kind()
if (k == reflect.Ptr ||
k == reflect.Interface ||
k == reflect.Map ||
k == reflect.Slice ||
k == reflect.Array) && val.IsNil() {
return nil
}
return input
}

decodeNil := d.config.DecodeNil && d.config.DecodeHook != nil

if input == nil {
// Decodes an unknown data type into a specific reflection value.
func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error {
var (
inputVal = reflect.ValueOf(input)
outputKind = getKind(outVal)
decodeNil = d.config.DecodeNil && d.cachedDecodeHook != nil
)
input = toRealNil(input)
if input == nil || !inputVal.IsValid() {
// If the data is nil, then we don't set anything, unless ZeroFields is set
// to true.
if d.config.ZeroFields {
Expand All @@ -467,40 +478,37 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
}
}

if !decodeNil {
return nil
}
}

if !inputVal.IsValid() {
if !decodeNil {
// If the input value is invalid, then we just set the value
// to be the zero value.
outVal.Set(reflect.Zero(outVal.Type()))
if d.config.Metadata != nil && name != "" {
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
}
return nil
}

// If we get here, we have an untyped nil so the type of the input is assumed.
// We do this because all subsequent code requires a valid value for inputVal.
var mapVal map[string]interface{}
inputVal = reflect.MakeMap(reflect.TypeOf(mapVal))
}

if d.cachedDecodeHook != nil {
// We have a DecodeHook, so let's pre-process the input.
if !inputVal.IsValid() {
// Hooks need a valid inputVal, so reset it to zero value of outVal type.
switch outputKind {
case reflect.Struct, reflect.Map:
var mapVal map[string]interface{}
inputVal = reflect.ValueOf(mapVal)
case reflect.Slice, reflect.Array:
var sliceVal []interface{}
inputVal = reflect.ValueOf(sliceVal)
default:
inputVal = reflect.Zero(outVal.Type())
}
}
var err error
input, err = d.cachedDecodeHook(inputVal, outVal)
if err != nil {
return fmt.Errorf("error decoding '%s': %w", name, err)
}
}
if toRealNil(input) == nil {
return nil
}

var err error
outputKind := getKind(outVal)
addMetaKey := true
switch outputKind {
case reflect.Bool:
Expand Down

0 comments on commit cd1c879

Please sign in to comment.