support squashing embedded structs
diff --git a/mapstructure.go b/mapstructure.go
index 2941a51..70da05b 100644
--- a/mapstructure.go
+++ b/mapstructure.go
@@ -333,6 +333,29 @@
fieldType := valType.Field(i)
fieldName := fieldType.Name
+ if fieldType.Anonymous {
+ // We have an embedded field. We "squash" the fields down if
+ // specified in the tag.
+ squash := false
+ tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
+ for _, tag := range tagParts[1:] {
+ if tag == "squash" {
+ squash = true
+ break
+ }
+ }
+
+ if squash {
+ inner := val.FieldByName(fieldName)
+ err := d.decodeStruct(name, data, inner)
+ if err != nil {
+ errors = appendErrors(errors, err)
+ }
+
+ continue
+ }
+ }
+
tagValue := fieldType.Tag.Get(d.config.TagName)
if tagValue != "" {
fieldName = tagValue
diff --git a/mapstructure_examples_test.go b/mapstructure_examples_test.go
index 5faae12..d7f3d00 100644
--- a/mapstructure_examples_test.go
+++ b/mapstructure_examples_test.go
@@ -71,16 +71,16 @@
func ExampleDecode_metadata() {
type Person struct {
- Name string
- Age int
+ Name string
+ Age int
}
// This input can come from anywhere, but typically comes from
// something like decoding JSON where we're not quite sure of the
// struct initially.
input := map[string]interface{}{
- "name": "Mitchell",
- "age": 91,
+ "name": "Mitchell",
+ "age": 91,
"email": "foo@bar.com",
}
@@ -91,7 +91,7 @@
var result Person
config := &DecoderConfig{
Metadata: &md,
- Result: &result,
+ Result: &result,
}
decoder, err := NewDecoder(config)
diff --git a/mapstructure_test.go b/mapstructure_test.go
index b3e1363..12e31c2 100644
--- a/mapstructure_test.go
+++ b/mapstructure_test.go
@@ -15,6 +15,16 @@
Vdata interface{}
}
+type Embedded struct {
+ Basic
+ Vunique string
+}
+
+type EmbeddedSquash struct {
+ Basic `mapstructure:",squash"`
+ Vunique string
+}
+
type Map struct {
Vfoo string
Vother map[string]string
@@ -104,6 +114,55 @@
}
}
+func TestDecode_Embedded(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vstring": "foo",
+ "Basic": map[string]interface{}{
+ "vstring": "innerfoo",
+ },
+ "vunique": "bar",
+ }
+
+ var result Embedded
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err.Error())
+ }
+
+ if result.Vstring != "innerfoo" {
+ t.Errorf("vstring value should be 'innerfoo': %#v", result.Vstring)
+ }
+
+ if result.Vunique != "bar" {
+ t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+ }
+}
+
+func TestDecode_EmbeddedSquash(t *testing.T) {
+ t.Parallel()
+
+ input := map[string]interface{}{
+ "vstring": "foo",
+ "vunique": "bar",
+ }
+
+ var result EmbeddedSquash
+ err := Decode(input, &result)
+ if err != nil {
+ t.Fatalf("got an err: %s", err.Error())
+ }
+
+ if result.Vstring != "foo" {
+ t.Errorf("vstring value should be 'foo': %#v", result.Vstring)
+ }
+
+ if result.Vunique != "bar" {
+ t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+ }
+}
+
func TestDecode_NonStruct(t *testing.T) {
t.Parallel()
@@ -128,13 +187,13 @@
input := map[string]interface{}{
"vstring": "hello",
- "foo": "bar",
+ "foo": "bar",
}
var result Basic
config := &DecoderConfig{
ErrorUnused: true,
- Result: &result,
+ Result: &result,
}
decoder, err := NewDecoder(config)