#358: access nested array values using get

This commit is contained in:
laxman.vallandas 2017-06-15 13:50:46 +00:00
parent 0967fc9ace
commit 68a8b52d7b

View file

@ -28,6 +28,7 @@ import (
"os"
"path/filepath"
"reflect"
"strconv"
"strings"
"time"
@ -53,7 +54,7 @@ func init() {
type remoteConfigFactory interface {
Get(rp RemoteProvider) (io.Reader, error)
Watch(rp RemoteProvider) (io.Reader, error)
WatchChannel(rp RemoteProvider)(<-chan *RemoteResponse, chan bool)
WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool)
}
// RemoteConfig is optional, see the remote package
@ -492,6 +493,56 @@ func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []
return nil
}
func (v *Viper) searchMapWithArrayPrefix(source map[string]interface{}, path []string) interface{} {
if len(path) == 0 {
return source
}
next, ok := source[path[0]]
if ok {
// Immediate Key Value
if len(path) == 1 {
return next
}
// Value from Nested key
switch next.(type) {
case map[interface{}]interface{}:
return v.searchMapWithArrayPrefix(cast.ToStringMap(next), path[1:])
case map[string]interface{}:
// Type assertion is safe here since it is only reached
// if the type of `next` is the same as the type being asserted
return v.searchMapWithArrayPrefix(next.(map[string]interface{}), path[1:])
case []interface{}:
v1 := cast.ToSlice(next)
for k2, v2 := range v1 {
if reflect.TypeOf(v2).Kind() == reflect.Map {
if _, err := strconv.ParseInt(path[1], 10, 64); err == nil {
if strconv.Itoa(k2) == path[1] {
return v.searchMapWithArrayPrefix(cast.ToStringMap(v2), path[1:])
}
} else {
return v.searchMapWithArrayPrefix(cast.ToStringMap(v2), path[1:])
}
} else {
if _, err := strconv.ParseInt(path[1], 10, 64); err == nil {
if strconv.Itoa(k2) == path[1] {
return v2
}
}
}
}
default:
return nil
}
} else {
if _, err := strconv.ParseInt(path[0], 10, 64); err == nil {
return v.searchMapWithArrayPrefix(source, path[1:])
}
}
return nil
}
// isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere
// on its path in the map.
// e.g., if "foo.bar" has a value in the given map, it “shadows”
@ -858,7 +909,6 @@ func (v *Viper) BindEnv(input ...string) error {
// Viper will check to see if an alias exists first.
// Note: this assumes a lower-cased key given.
func (v *Viper) find(lcaseKey string) interface{} {
var (
val interface{}
exists bool
@ -932,6 +982,11 @@ func (v *Viper) find(lcaseKey string) interface{} {
if val != nil {
return val
}
val = v.searchMapWithArrayPrefix(v.config, path)
if val != nil {
return val
}
if nested && v.isPathShadowedInDeepMap(path, v.config) != "" {
return nil
}