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

Commit

Permalink
Fix squash decoder option to squash only embedded fields
Browse files Browse the repository at this point in the history
Fixes #193
  • Loading branch information
uvw committed May 21, 2020
1 parent 14428cd commit 1d69ed7
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 3 deletions.
4 changes: 2 additions & 2 deletions mapstructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
keyName := f.Name

// If Squash is set in the config, we squash the field down.
squash := d.config.Squash && v.Kind() == reflect.Struct
squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
// Determine the name of the key in the map
if index := strings.Index(tagValue, ","); index != -1 {
if tagValue[:index] == "-" {
Expand Down Expand Up @@ -1187,7 +1187,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
fieldKind := fieldType.Type.Kind()

// If "squash" is specified in the tag, we squash the field down.
squash := d.config.Squash && fieldKind == reflect.Struct
squash := d.config.Squash && fieldKind == reflect.Struct && fieldType.Anonymous
remain := false

// We always parse the tags cause we're looking for other tags too
Expand Down
71 changes: 70 additions & 1 deletion mapstructure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ type EmbeddedSquash struct {
Vunique string
}

type EmbeddedAndNamed struct {
Basic
Named Basic
Vunique string
}

type SliceAlias []string

type EmbeddedSlice struct {
Expand Down Expand Up @@ -612,9 +618,12 @@ func TestDecode_EmbeddedSquashConfig(t *testing.T) {
input := map[string]interface{}{
"vstring": "foo",
"vunique": "bar",
"Named": map[string]interface{}{
"vstring": "baz",
},
}

var result Embedded
var result EmbeddedAndNamed
config := &DecoderConfig{
Squash: true,
Result: &result,
Expand All @@ -637,6 +646,66 @@ func TestDecode_EmbeddedSquashConfig(t *testing.T) {
if result.Vunique != "bar" {
t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
}

if result.Named.Vstring != "baz" {
t.Errorf("Named.vstring value should be 'baz': %#v", result.Named.Vstring)
}
}

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

input := EmbeddedAndNamed{
Basic: Basic{Vstring: "foo"},
Named: Basic{Vstring: "baz"},
Vunique: "bar",
}

result := map[string]interface{}{}
config := &DecoderConfig{
Squash: true,
Result: &result,
}
decoder, err := NewDecoder(config)
if err != nil {
t.Fatalf("got an err: %s", err.Error())
}

err = decoder.Decode(input)
if err != nil {
t.Fatalf("got an err: %s", err.Error())
}

if _, ok := result["Basic"]; ok {
t.Error("basic should not be present in map")
}

v, ok := result["Vstring"]
if !ok {
t.Error("vstring should be present in map")
} else if !reflect.DeepEqual(v, "foo") {
t.Errorf("vstring value should be 'foo': %#v", v)
}

v, ok = result["Vunique"]
if !ok {
t.Error("vunique should be present in map")
} else if !reflect.DeepEqual(v, "bar") {
t.Errorf("vunique value should be 'bar': %#v", v)
}

v, ok = result["Named"]
if !ok {
t.Error("Named should be present in map")
} else {
named := v.(map[string]interface{})
v, ok := named["Vstring"]
if !ok {
t.Error("Named: vstring should be present in map")
} else if !reflect.DeepEqual(v, "baz") {
t.Errorf("Named: vstring should be 'baz': %#v", v)
}
}
}

func TestDecode_SquashOnNonStructType(t *testing.T) {
Expand Down

0 comments on commit 1d69ed7

Please sign in to comment.