Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit fd87e0d

Browse files
committed
Support custom name matchers
1 parent 4664f9e commit fd87e0d

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

mapstructure.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,10 @@ type DecoderConfig struct {
258258
// The tag name that mapstructure reads for field names. This
259259
// defaults to "mapstructure"
260260
TagName string
261+
262+
// MatchName is the function used to match the map key to the struct
263+
// field name or tag. Defaults to `strings.EqualFold`.
264+
MatchName func(mapKey, fieldName string) bool
261265
}
262266

263267
// A Decoder takes a raw interface value and turns it into structured
@@ -1340,7 +1344,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
13401344
continue
13411345
}
13421346

1343-
if strings.EqualFold(mK, fieldName) {
1347+
if d.matchName(mK, fieldName) {
13441348
rawMapKey = dataValKey
13451349
rawMapVal = dataVal.MapIndex(dataValKey)
13461350
break
@@ -1428,6 +1432,13 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
14281432
return nil
14291433
}
14301434

1435+
func (d *Decoder) matchName(mapKey, fieldName string) bool {
1436+
if d.config.MatchName != nil {
1437+
return d.config.MatchName(mapKey, fieldName)
1438+
}
1439+
return strings.EqualFold(mapKey, fieldName)
1440+
}
1441+
14311442
func isEmptyValue(v reflect.Value) bool {
14321443
switch getKind(v) {
14331444
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:

mapstructure_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2431,6 +2431,49 @@ func TestDecode_mapToStruct(t *testing.T) {
24312431
}
24322432
}
24332433

2434+
func TestDecoder_MatchName(t *testing.T) {
2435+
t.Parallel()
2436+
2437+
type Target struct {
2438+
FirstMatch string `mapstructure:"first_match"`
2439+
SecondMatch string
2440+
NoMatch string `mapstructure:"no_match"`
2441+
}
2442+
2443+
input := map[string]interface{}{
2444+
"first_match": "foo",
2445+
"SecondMatch": "bar",
2446+
"NO_MATCH": "baz",
2447+
}
2448+
2449+
expected := Target{
2450+
FirstMatch: "foo",
2451+
SecondMatch: "bar",
2452+
}
2453+
2454+
var actual Target
2455+
config := &DecoderConfig{
2456+
Result: &actual,
2457+
MatchName: func(mapKey, fieldName string) bool {
2458+
return mapKey == fieldName
2459+
},
2460+
}
2461+
2462+
decoder, err := NewDecoder(config)
2463+
if err != nil {
2464+
t.Fatalf("err: %s", err)
2465+
}
2466+
2467+
err = decoder.Decode(input)
2468+
if err != nil {
2469+
t.Fatalf("err: %s", err)
2470+
}
2471+
2472+
if !reflect.DeepEqual(expected, actual) {
2473+
t.Fatalf("Decode() expected: %#v, got: %#v", expected, actual)
2474+
}
2475+
}
2476+
24342477
func testSliceInput(t *testing.T, input map[string]interface{}, expected *Slice) {
24352478
var result Slice
24362479
err := Decode(input, &result)

0 commit comments

Comments
 (0)