DecodeHook
diff --git a/mapstructure.go b/mapstructure.go index dae28dd..20941e1 100644 --- a/mapstructure.go +++ b/mapstructure.go
@@ -19,6 +19,14 @@ // DecoderConfig is the configuration that is used to create a new decoder // and allows customization of various aspects of decoding. type DecoderConfig struct { + // DecodeHook, if set, will be called before any decoding and any + // type conversion (if WeaklyTypedInput is on). This lets you modify + // the values before they're set down onto the resulting struct. + // + // If an error is returned, the entire decode will fail with that + // error. + DecodeHook func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) + // If ErrorUnused is true, then it is an error for there to exist // keys in the original map that were unused in the decoding process // (extra keys). @@ -144,9 +152,17 @@ return nil } + if d.config.DecodeHook != nil { + // We have a DecodeHook, so let's pre-process the data. + var err error + data, err = d.config.DecodeHook(d.getKind(dataVal), d.getKind(val), data) + if err != nil { + return err + } + } + var err error dataKind := d.getKind(val) - switch dataKind { case reflect.Bool: err = d.decodeBool(name, data, val)
diff --git a/mapstructure_test.go b/mapstructure_test.go index 53aa5f8..8d86099 100644 --- a/mapstructure_test.go +++ b/mapstructure_test.go
@@ -219,6 +219,42 @@ } } +func TestDecode_DecodeHook(t *testing.T) { + t.Parallel() + + input := map[string]interface{}{ + "vint": "WHAT", + } + + decodeHook := func(from reflect.Kind, to reflect.Kind, v interface{}) (interface{}, error) { + if from == reflect.String && to != reflect.String { + return 5, nil + } + + return v, nil + } + + var result Basic + config := &DecoderConfig{ + DecodeHook: decodeHook, + Result: &result, + } + + decoder, err := NewDecoder(config) + if err != nil { + t.Fatalf("err: %s", err) + } + + err = decoder.Decode(input) + if err != nil { + t.Fatalf("got an err: %s", err) + } + + if result.Vint != 5 { + t.Errorf("vint should be 5: %#v", result.Vint) + } +} + func TestDecode_Nil(t *testing.T) { t.Parallel()