Getting Values From Viper
In Viper, there are a few ways to get a value depending on the value’s type.The following functions and methods exist:
Get(key string) : interface{}
GetBool(key string) : bool
GetFloat64(key string) : float64
GetInt(key string) : int
GetString(key string) : string
GetStringMap(key string) : map[string]interface{}
GetStringMapString(key string) : map[string]string
GetStringSlice(key string) : []string
GetTime(key string) : time.Time
GetDuration(key string) : time.Duration
IsSet(key string) : bool
AllSettings() : map[string]interface{}
One important thing to recognize is that each Get function will return a zerovalue if it’s not found. To check if a given key exists, theIsSet()
methodhas been provided.
Example:
- viper.GetString("logfile") // case-insensitive Setting & Getting
- if viper.GetBool("verbose") {
- fmt.Println("verbose enabled")
- }
Accessing nested keys
The accessor methods also accept formatted paths to deeply nested keys. Forexample, if the following JSON file is loaded:
- {
- "host": {
- "address": "localhost",
- "port": 5799
- },
- "datastore": {
- "metric": {
- "host": "127.0.0.1",
- "port": 3099
- },
- "warehouse": {
- "host": "198.0.0.1",
- "port": 2112
- }
- }
- }
Viper can access a nested field by passing a .
delimited path of keys:
- GetString("datastore.metric.host") // (returns "127.0.0.1")
This obeys the precedence rules established above; the search for the pathwill cascade through the remaining configuration registries until found.
For example, given this configuration file, both datastore.metric.host
anddatastore.metric.port
are already defined (and may be overridden). If in additiondatastore.metric.protocol
was defined in the defaults, Viper would also find it.
However, if datastore.metric
was overridden (by a flag, an environment variable,the Set()
method, …) with an immediate value, then all sub-keys ofdatastore.metric
become undefined, they are “shadowed” by the higher-priorityconfiguration level.
Lastly, if there exists a key that matches the delimited key path, its valuewill be returned instead. E.g.
- {
- "datastore.metric.host": "0.0.0.0",
- "host": {
- "address": "localhost",
- "port": 5799
- },
- "datastore": {
- "metric": {
- "host": "127.0.0.1",
- "port": 3099
- },
- "warehouse": {
- "host": "198.0.0.1",
- "port": 2112
- }
- }
- }
- GetString("datastore.metric.host") // returns "0.0.0.0"
Extract sub-tree
Extract sub-tree from Viper.
For example, viper
represents:
- app:
- cache1:
- max-items: 100
- item-size: 64
- cache2:
- max-items: 200
- item-size: 80
After executing:
- subv := viper.Sub("app.cache1")
subv
represents:
- max-items: 100
- item-size: 64
Suppose we have:
- func NewCache(cfg *Viper) *Cache {...}
which creates a cache based on config information formatted as subv
.Now it’s easy to create these 2 caches separately as:
- cfg1 := viper.Sub("app.cache1")
- cache1 := NewCache(cfg1)
- cfg2 := viper.Sub("app.cache2")
- cache2 := NewCache(cfg2)
Unmarshaling
You also have the option of Unmarshaling all or a specific value to a struct, map,etc.
There are two methods to do this:
Unmarshal(rawVal interface{}) : error
UnmarshalKey(key string, rawVal interface{}) : error
Example:
- type config struct {
- Port int
- Name string
- PathMap string `mapstructure:"path_map"`
- }
- var C config
- err := Unmarshal(&C)
- if err != nil {
- t.Fatalf("unable to decode into struct, %v", err)
- }
Marshalling to string
You may need to marhsal all the settings held in viper into a string rather than write them to a file.You can use your favorite format's marshaller with the config returned by AllSettings()
.
- import (
- yaml "gopkg.in/yaml.v2"
- // ...
- )
- func yamlStringSettings() string {
- c := viper.AllSettings()
- bs, err := yaml.Marshal(c)
- if err != nil {
- t.Fatalf("unable to marshal config to YAML: %v", err)
- }
- return string(bs)
- }