Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow decoding embedded struct pointer in decodeMapFromStruct #199

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions mapstructure.go
Expand Up @@ -894,6 +894,11 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
keyName = tagValue
}

// This is an embedded struct pointer, handle it as a embedded struct below
if v.Kind() == reflect.Ptr && reflect.Indirect(v).Kind() == reflect.Struct {
v = reflect.Indirect(v)
}

switch v.Kind() {
// this is an embedded struct, so handle it differently
case reflect.Struct:
Expand Down
38 changes: 36 additions & 2 deletions mapstructure_test.go
Expand Up @@ -1352,6 +1352,41 @@ func TestNestedTypePointer(t *testing.T) {
}
}

func TestNestedTypeStructPointer(t *testing.T) {
t.Parallel()

nestedInput := Basic{
Vstring: "foo",
Vint: 42,
Vbool: true,
}
input := NestedPointer{
Vfoo: "foo",
Vbar: &nestedInput,
}

// nestedResult is validated in TestBasicTypes
nestedResult := map[string]interface{}{}
err := Decode(nestedInput, &nestedResult)
if err != nil {
t.Fatalf("got an err: %s", err.Error())
}

result := map[string]interface{}{}
err = Decode(input, &result)
if err != nil {
t.Fatalf("got an err: %s", err.Error())
}

expected := map[string]interface{}{
"Vfoo": "foo",
"Vbar": nestedResult,
}
if !reflect.DeepEqual(result, expected) {
t.Errorf("bad: %#v", result)
}
}

// Test for issue #46.
func TestNestedTypeInterface(t *testing.T) {
t.Parallel()
Expand Down Expand Up @@ -2316,7 +2351,7 @@ func TestDecode_StructTaggedWithOmitempty_KeepNonEmptyValues(t *testing.T) {
VisibleMapField: nil,
OmitMapField: map[string]interface{}{"k": "v"},
NestedField: nil,
OmitNestedField: &Nested{},
OmitNestedField: nil,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this change intended? Looks a bit suspicious to me.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is intended. This PR change the behavior of struct pointer to allow decoding them. This means that &Nested{} is no longer considered nil once decoded; it will be a empty map[string]interface{}. Since this is not what is tested here, a separate unit test (TestNestedTypeStructPointer) was added just for this and this test was modified to pass nil so that it is not overloaded.

}

var emptySlice []interface{}
Expand All @@ -2334,7 +2369,6 @@ func TestDecode_StructTaggedWithOmitempty_KeepNonEmptyValues(t *testing.T) {
"visible-map": emptyMap,
"omittable-map": map[string]interface{}{"k": "v"},
"visible-nested": emptyNested,
"omittable-nested": &Nested{},
}

actual := &map[string]interface{}{}
Expand Down