diff --git a/mapstructure.go b/mapstructure.go index daea3318..d81f3341 100644 --- a/mapstructure.go +++ b/mapstructure.go @@ -198,6 +198,12 @@ type DecoderConfig struct { // The tag name that mapstructure reads for field names. This // defaults to "mapstructure" TagName string + + // Extra transformation methods to match field name from map to struct. + // Options: + // - snake (snake_case: all characters in lower case and concatenating words by '_') + // - kebab (kebab-case: all characters in lower case and concatenating words by '-' ) + FieldNameTransFormMethod string } // A Decoder takes a raw interface value and turns it into structured @@ -1205,7 +1211,16 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e continue } - if strings.EqualFold(mK, fieldName) { + matched := strings.EqualFold(mK, fieldName) + + switch d.config.FieldNameTransFormMethod { + case "snake": + matched = matched || strings.EqualFold(strings.ReplaceAll(mK, "_", ""), fieldName) + case "kebab": + matched = matched || strings.EqualFold(strings.ReplaceAll(mK, "-", ""), fieldName) + } + + if matched { rawMapKey = dataValKey rawMapVal = dataVal.MapIndex(dataValKey) break diff --git a/mapstructure_test.go b/mapstructure_test.go index 927f0767..87381b78 100644 --- a/mapstructure_test.go +++ b/mapstructure_test.go @@ -1044,6 +1044,34 @@ func TestDecoder_ErrorUnused_NotSetable(t *testing.T) { } } +func TestDecode_FieldNameTransForm(t *testing.T) { + t.Parallel() + + input := map[string]interface{}{ + "v_string": "WHAT", + } + + var result Basic + config := &DecoderConfig{ + Result: &result, + FieldNameTransFormMethod: "snake", + } + + 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.Vstring != "WHAT" { + t.Errorf("vstring should be WHAT: %#v", result.Vstring) + } +} + func TestMap(t *testing.T) { t.Parallel()