Repository: spf13/viper (Go)

Issue:
PR #673 broke my code. The nested structures don't get the default values correctly
I wrote a short example to illustrate the problem.

```Go
package main

import (
	"fmt"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

type Config struct {
	Foo struct {
		Bar string
	}
}

func main() {
	config := &Config{}
	cmdRoot := &cobra.Command{
		Run: func(*cobra.Command, []string) {
			viper.Unmarshal(config)
		},
	}
	flags := cmdRoot.PersistentFlags()
	flags.String("foo.bar", "hello-world", "test default value fo foo.bar")
	viper.BindPFlags(flags)
	cmdRoot.Execute()
	fmt.Printf("foo.bar: [%v]\n", config.Foo.Bar)
}
```

The above code I expected to get the output is:

```
foo.bar: [hello-world]
```

This has worked very well in the past. But since PR #673, it can't work properly, it will output:

```
foo.bar: []
```

I checked the contents of the PR #673 , I can't fully understand the changes. But I believe the changes blocked my previous usage.

How should I modify my code to fit the new version of viper?


Discussion:
@flw-cn Thanks for opening this PR. It seems that the linked PR indeed introduced a regression.
It looks like this only affects flags? The new `keyComponents` function should always return as many components as possible, ie: it will favour `[foo bar]` over `[foo.bar]`. For some reason, it is not seeing `foo` when it is only set by a flag value so it then sets `foo.bar` as the last resort which then doesn't get unmarshalled correctly.
I explained in #673 why that's the case. Defaults and explicit sets are also affected. Basically everything that's not a nested structure (config files, key value stores)
I'm not seeing the same behaviour for `SetDefault` or `Set`...
```go
package viper

import (
	"github.com/spf13/pflag"
	"github.com/stretchr/testify/assert"
	"testing"
)

type Config struct {
	Foo struct {
		Bar string
	}
}

func Test673_PFlags(t *testing.T) {
	v := New()
	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
	flags.String("foo.bar", "cobra_flag", "")
	assert.NoError(t, v.BindPFlags(flags))

	config := &Config{}
	assert.NoError(t, v.Unmarshal(config))
	assert.Equal(t, "cobra_flag", config.Foo.Bar)
}

func Test673_Default(t *testing.T) {
	v := New()
	config := &Config{}
	v.SetDefault("foo.bar", "default")
	assert.NoError(t, v.Unmarshal(config))
	assert.Equal(t, "default", config.Foo.Bar)
}

func Test673_Set(t *testing.T) {
	v := New()
	config := &Config{}
	v.SetDefault("foo.bar", "default")
	v.Set("foo.bar", "override")
	assert.NoError(t, v.Unmarshal(config))
	assert.Equal(t, "override", config.Foo.Bar)
}
```
Currently for me, the `PFlags` test is failing but the other two are okay; am I testing the right thing here?



Patch:
diff --git a/viper.go b/viper.go
index 491799c7c..5133de792 100644
--- a/viper.go
+++ b/viper.go
@@ -1789,24 +1789,6 @@ outer:
 	return shadow
 }
 
-// Converts a fully qualified map key into a list of relative
-// map keys, allowing for keys to contain the delimiter themselves
-func keyComponents(v *Viper, key string) []string {
-	var result []string
-	components := strings.Split(key, v.keyDelim)
-	for index := 0; index < len(components); index++ {
-		potentialKey := strings.Join(components[0:index], v.keyDelim)
-		if v.Get(potentialKey) != nil {
-			result = append(result, potentialKey)
-		}
-	}
-	result = append(result, key)
-	for i := len(result) - 1; i > 0; i-- {
-		result[i] = strings.Replace(result[i], result[i-1]+v.keyDelim, "", 1)
-	}
-	return result
-}
-
 // AllSettings merges all settings and returns them as a map[string]interface{}.
 func AllSettings() map[string]interface{} { return v.AllSettings() }
 func (v *Viper) AllSettings() map[string]interface{} {
@@ -1819,7 +1801,7 @@ func (v *Viper) AllSettings() map[string]interface{} {
 			// check just in case anything changes
 			continue
 		}
-		path := keyComponents(v, k)
+		path := strings.Split(k, v.keyDelim)
 		lastKey := strings.ToLower(path[len(path)-1])
 		deepestMap := deepSearch(m, path[0:len(path)-1])
 		// set innermost value


Test patch:
diff --git a/viper_test.go b/viper_test.go
index 3c15ed26f..f8364a044 100644
--- a/viper_test.go
+++ b/viper_test.go
@@ -46,10 +46,6 @@ clothing:
 age: 35
 eyes : brown
 beard: true
-emails:
-  steve@hacker.com:
-    created: 01/02/03
-    active: true
 `)
 
 var yamlExampleWithExtras = []byte(`Existing: true
@@ -254,7 +250,7 @@ func initDirs(t *testing.T) (string, string, func()) {
 	}
 }
 
-//stubs for PFlag Values
+// stubs for PFlag Values
 type stringValue string
 
 func newStringValue(val string, p *string) *stringValue {
@@ -535,8 +531,6 @@ func TestAllKeys(t *testing.T) {
 		"title_dotenv",
 		"type_dotenv",
 		"name_dotenv",
-		"emails.steve@hacker.com.active",
-		"emails.steve@hacker.com.created",
 	}
 	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
 	all := map[string]interface{}{
@@ -547,13 +541,7 @@ func TestAllKeys(t *testing.T) {
 		},
 		"title": "TOML Example",
 		"ppu":   0.55,
-		"emails": map[string]interface{}{
-			"steve@hacker.com": map[string]interface{}{
-				"active":  true,
-				"created": "01/02/03",
-			},
-		},
-		"eyes": "brown",
+		"eyes":  "brown",
 		"clothing": map[string]interface{}{
 			"trousers": "denim",
 			"jacket":   "leather",
@@ -864,7 +852,7 @@ func TestBindPFlag(t *testing.T) {
 	assert.Equal(t, testString, Get("testvalue"))
 
 	flag.Value.Set("testing_mutate")
-	flag.Changed = true //hack for pflag usage
+	flag.Changed = true // hack for pflag usage
 
 	assert.Equal(t, "testing_mutate", Get("testvalue"))
 
@@ -1316,10 +1304,6 @@ clothing:
   pants:
     size: large
   trousers: denim
-emails:
-  steve@hacker.com:
-    active: true
-    created: 01/02/03
 eyes: brown
 hacker: true
 hobbies:
@@ -1856,6 +1840,23 @@ func TestWatchFile(t *testing.T) {
 
 }
 
+func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
+	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
+	flags.String("foo.bar", "cobra_flag", "")
+
+	v := New()
+	assert.NoError(t, v.BindPFlags(flags))
+
+	config := &struct {
+		Foo struct {
+			Bar string
+		}
+	}{}
+
+	assert.NoError(t, v.Unmarshal(config))
+	assert.Equal(t, "cobra_flag", config.Foo.Bar)
+}
+
 func BenchmarkGetBool(b *testing.B) {
 	key := "BenchmarkGetBool"
 	v = New()