Skip to content

Commit 8eeb752

Browse files
committed
feat: optional root object name
1 parent af2cf2a commit 8eeb752

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

mapstructure.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,10 @@ type DecoderConfig struct {
300300
// defaults to "mapstructure"
301301
TagName string
302302

303+
// RootName specifies the name to use for the root element in error messages. For example:
304+
// '<rootName>' has unset fields: <fieldName>
305+
RootName string
306+
303307
// The option of the value in the tag that indicates a field should
304308
// be squashed. This defaults to "squash".
305309
SquashTagOption string
@@ -465,7 +469,7 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
465469
// Decode decodes the given raw interface to the target pointer specified
466470
// by the configuration.
467471
func (d *Decoder) Decode(input any) error {
468-
err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
472+
err := d.decode(d.config.RootName, input, reflect.ValueOf(d.config.Result).Elem())
469473

470474
// Retain some of the original behavior when multiple errors ocurr
471475
var joinedErr interface{ Unwrap() []error }

mapstructure_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3675,6 +3675,58 @@ func TestDecode_structArrayDeepMap(t *testing.T) {
36753675
}
36763676
}
36773677

3678+
func TestDecoder_RootName(t *testing.T) {
3679+
t.Parallel()
3680+
3681+
input := map[string]any{
3682+
"surname": "green",
3683+
"relation": map[string]any{
3684+
"surname": "black",
3685+
},
3686+
}
3687+
3688+
var result struct {
3689+
Name string `mapstructure:"name"`
3690+
Relation struct {
3691+
Name string `mapstructure:"name"`
3692+
} `mapstructure:"relation"`
3693+
}
3694+
3695+
decoder, err := NewDecoder(&DecoderConfig{
3696+
ErrorUnset: true,
3697+
ErrorUnused: true,
3698+
Result: &result,
3699+
RootName: "root",
3700+
})
3701+
if err != nil {
3702+
t.Fatalf("err: %s", err)
3703+
}
3704+
3705+
err = decoder.Decode(input)
3706+
if err == nil {
3707+
t.Fatal("expected error")
3708+
}
3709+
3710+
expectedErrors := []string{
3711+
`'root.relation' has invalid keys: surname`,
3712+
`'root.relation' has unset fields: name`,
3713+
`'root' has invalid keys: surname`,
3714+
`'root' has unset fields: name`,
3715+
}
3716+
3717+
failed := false
3718+
3719+
for _, expectedErr := range expectedErrors {
3720+
if !strings.Contains(err.Error(), expectedErr) {
3721+
failed = true
3722+
}
3723+
}
3724+
3725+
if failed {
3726+
t.Errorf("unexpected error message, got: %s", err.Error())
3727+
}
3728+
}
3729+
36783730
func stringPtr(v string) *string { return &v }
36793731
func intPtr(v int) *int { return &v }
36803732
func uintPtr(v uint) *uint { return &v }

0 commit comments

Comments
 (0)