#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" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strconv"
"strings" "strings"
"time" "time"
@ -53,7 +54,7 @@ func init() {
type remoteConfigFactory interface { type remoteConfigFactory interface {
Get(rp RemoteProvider) (io.Reader, error) Get(rp RemoteProvider) (io.Reader, error)
Watch(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 // RemoteConfig is optional, see the remote package
@ -492,6 +493,56 @@ func (v *Viper) searchMapWithPathPrefixes(source map[string]interface{}, path []
return nil 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 // isPathShadowedInDeepMap makes sure the given path is not shadowed somewhere
// on its path in the map. // on its path in the map.
// e.g., if "foo.bar" has a value in the given map, it “shadows” // 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. // Viper will check to see if an alias exists first.
// Note: this assumes a lower-cased key given. // Note: this assumes a lower-cased key given.
func (v *Viper) find(lcaseKey string) interface{} { func (v *Viper) find(lcaseKey string) interface{} {
var ( var (
val interface{} val interface{}
exists bool exists bool
@ -932,6 +982,11 @@ func (v *Viper) find(lcaseKey string) interface{} {
if val != nil { if val != nil {
return val return val
} }
val = v.searchMapWithArrayPrefix(v.config, path)
if val != nil {
return val
}
if nested && v.isPathShadowedInDeepMap(path, v.config) != "" { if nested && v.isPathShadowedInDeepMap(path, v.config) != "" {
return nil return nil
} }