diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go index f68748c6..8bc2fed1 100644 --- a/core/mapping/unmarshaler.go +++ b/core/mapping/unmarshaler.go @@ -919,12 +919,16 @@ func (u *Unmarshaler) processNamedFieldWithoutValue(fieldType reflect.Type, valu } switch fieldKind { - case reflect.Array, reflect.Map, reflect.Slice: + case reflect.Array, reflect.Slice: if !opts.optional() { return u.processFieldNotFromString(fieldType, value, valueWithParent{ value: emptyMap, }, opts, fullName) } + case reflect.Map: + if !opts.optional() { + return newInitError(fullName) + } case reflect.Struct: if !opts.optional() { required, err := structValueRequired(u.key, derefedType) diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go index ae2aba0e..c281eb01 100644 --- a/core/mapping/unmarshaler_test.go +++ b/core/mapping/unmarshaler_test.go @@ -2432,6 +2432,42 @@ func TestUnmarshalMapOfStruct(t *testing.T) { } assert.Error(t, UnmarshalKey(m, &v)) }) + + t.Run("map set", func(t *testing.T) { + type Inner1 struct { + M map[string]string + } + assert.Error(t, UnmarshalKey(map[string]any{}, &Inner1{})) + assert.NoError(t, UnmarshalKey(map[string]any{ + "M": map[string]string{}, + }, &Inner1{})) + + type Inner2 struct { + Inner1 + } + assert.Error(t, UnmarshalKey(map[string]any{}, &Inner2{})) + assert.NoError(t, UnmarshalKey(map[string]any{ + "M": map[string]string{}, + }, &Inner2{})) + + type Inner3 struct { + C Inner1 + } + assert.Error(t, UnmarshalKey(map[string]any{}, &Inner3{})) + assert.NoError(t, UnmarshalKey(map[string]any{ + "C": map[string]any{ + "M": map[string]string{}, + }, + }, &Inner3{})) + + type Inner4 struct { + M map[string]string `json:",optional"` + } + assert.NoError(t, UnmarshalKey(map[string]any{}, &Inner4{})) + assert.NoError(t, UnmarshalKey(map[string]any{ + "M": map[string]string{}, + }, &Inner4{})) + }) } func TestUnmarshalSlice(t *testing.T) {