Repository: mitchellh/mapstructure (go)

Issue:
Squash embedded structs by default
To be consistent with "encoding/json", an embedded struct like

```
type Outer struct {
    Inner
}
```

should behave as though tagged with `mapstructure:",squash"`, unless explicitly given a name like `mapstructure:"Inner"`.

Example code demonstrating the inconsistency with "encoding/json":

```
package main

import (
    "encoding/json"
    "fmt"
    "github.com/mitchellh/mapstructure"
)

type Inner struct {
    Name string
}

type Outer struct {
    Inner // works if you add `mapstructure:",squash"`
}

func main() {
    input := Outer{Inner: Inner{Name: "mitchellh"}}
    fmt.Printf("input: %#v\n", input)

    bytes, err := json.Marshal(input)
    if err != nil { panic(err) }
    fmt.Printf("json: %v\n", string(bytes))

    var outputGood Outer
    err = json.Unmarshal(bytes, &outputGood)
    if err != nil { panic(err) }
    fmt.Printf("outputGood: %#v\n", outputGood)

    var tmp interface{}
    err = json.Unmarshal(bytes, &tmp)
    if err != nil { panic(err) }
    fmt.Printf("tmp: %#v\n", tmp)

    var outputBad Outer
    err = mapstructure.Decode(tmp, &outputBad)
    if err != nil { panic(err) }
    fmt.Printf("outputBad: %#v\n", outputBad)
}
```

The output is:

```
input: main.Outer{Inner:main.Inner{Name:"mitchellh"}}
json: {"Name":"mitchellh"}
outputGood: main.Outer{Inner:main.Inner{Name:"mitchellh"}}
tmp: map[string]interface {}{"Name":"mitchellh"}
outputBad: main.Outer{Inner:main.Inner{Name:""}}
```

The correct output would have:

```
outputBad: main.Outer{Inner:main.Inner{Name:"mitchellh"}}
```


Pull request:
Added decoder option for squashing embedded structs
Fixes #42

Patch:
diff --git a/mapstructure.go b/mapstructure.go
index 256ee63..6093dba 100644
--- a/mapstructure.go
+++ b/mapstructure.go
@@ -80,6 +80,14 @@ type DecoderConfig struct {
 	//
 	WeaklyTypedInput bool
 
+	// Squash will squash embedded structs.  A squash tag may also be
+	// added to an individual struct field using a tag.  For example:
+	//
+	//  type Parent struct {
+	//      Child `mapstructure:",squash"`
+	//  }
+	Squash bool
+
 	// Metadata is the struct that will contain extra metadata about
 	// the decoding. If this is nil, then no metadata will be tracked.
 	Metadata *Metadata
@@ -689,16 +697,19 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
 			keyName = tagParts[0]
 		}
 
+		// If Squash is set in the config, we squash the field down.
+		squash := d.config.Squash && v.Kind() == reflect.Struct
 		// If "squash" is specified in the tag, we squash the field down.
-		squash := false
-		for _, tag := range tagParts[1:] {
-			if tag == "squash" {
-				squash = true
-				break
+		if !squash {
+			for _, tag := range tagParts[1:] {
+				if tag == "squash" {
+					squash = true
+					break
+				}
+			}
+			if squash && v.Kind() != reflect.Struct {
+				return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
 			}
-		}
-		if squash && v.Kind() != reflect.Struct {
-			return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
 		}
 
 		switch v.Kind() {
@@ -1017,12 +1028,14 @@ 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 := false
-			tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
-			for _, tag := range tagParts[1:] {
-				if tag == "squash" {
-					squash = true
-					break
+			squash := d.config.Squash && fieldKind == reflect.Struct
+			if !squash {
+				tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
+				for _, tag := range tagParts[1:] {
+					if tag == "squash" {
+						squash = true
+						break
+					}
 				}
 			}
 


Test patch:
diff --git a/mapstructure_test.go b/mapstructure_test.go
index 27ac10e..b7d0793 100644
--- a/mapstructure_test.go
+++ b/mapstructure_test.go
@@ -450,6 +450,55 @@ func TestDecode_EmbeddedArray(t *testing.T) {
 	}
 }
 
+func TestDecode_EmbeddedNoSquash(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"vunique": "bar",
+	}
+
+	var result Embedded
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vstring != "" {
+		t.Errorf("vstring value should be empty: %#v", result.Vstring)
+	}
+
+	if result.Vunique != "bar" {
+		t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+	}
+}
+
+func TestDecode_EmbeddedPointerNoSquash(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"vunique": "bar",
+	}
+
+	result := EmbeddedPointer{
+		Basic: &Basic{},
+	}
+
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	if result.Vstring != "" {
+		t.Errorf("vstring value should be empty: %#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()
 
@@ -511,6 +560,39 @@ func TestDecodeFrom_EmbeddedSquash(t *testing.T) {
 	}
 }
 
+func TestDecode_EmbeddedSquashConfig(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"vunique": "bar",
+	}
+
+	var result Embedded
+	config := &DecoderConfig{
+		Squash: true,
+		Result: &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	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_SquashOnNonStructType(t *testing.T) {
 	t.Parallel()