Reputation: 425
How do I write the code below to get a string from my nested yaml struct?
Here is my yaml:
element:
- one:
url: http://test
nested: 123
- two:
url: http://test
nested: 123
weather:
- test:
zipcode: 12345
- ca:
zipcode: 90210
Here is example code
viper.SetConfigName("main_config")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
testvar := viper.GetString("element.one.url")
My problem:
I get a blank string when I print this. According to the docs, this is how you get a nested element. I suspect its not working because the elements are lists. Do I need to do a struct? I am not sure how to make one, especially if it needs to be nested.
Upvotes: 13
Views: 25898
Reputation: 1
config.go
package config
import (
"fmt"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
type User struct {
Username string `mapstructure:"username"`
Passwd string `mapstructure:"passwd"`
}
type Config struct {
Account []User `mapstructure:"account"`
}
var AppConfig Config
func init() {
viperCfg := viper.New()
viperCfg.SetConfigName("config")
viperCfg.SetConfigType("yaml")
viperCfg.AddConfigPath("../")
err := viperCfg.ReadInConfig()
if err != nil {
panic(err)
}
// fmt.Println(viperCfg)
err = viperCfg.Unmarshal(&AppConfig)
if err != nil {
panic(err)
}
viperCfg.WatchConfig()
viperCfg.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
if err = viperCfg.Unmarshal(&AppConfig); err != nil {
fmt.Println(err)
}
})
}
func GetConfig() Config {
return AppConfig
}
account:
- username: abc
passwd: 123
- username: def
passwd: 123
config_test.go
package config
import (
"testing"
)
func TestConfig(t *testing.T) {
config := AppConfig
t.Log("username:", config.Account[0].Username)
t.Log("passwd:", config.Account[0].Passwd)
}
result
Running tool: /usr/local/go/bin/go test -timeout 30s -run ^TestConfig$ goask/config
=== RUN TestConfig
/Users/xzeu/go/src/digask/config/config_test.go:9: username: abc
/Users/xzeu/go/src/digask/config/config_test.go:10: passwd: 123
--- PASS: TestConfig (0.00s)
PASS
ok goask/config 0.968s
Upvotes: 0
Reputation: 3422
You can unmarshal a nested configuration file.
main.go
package main
import (
"fmt"
"github.com/spf13/viper"
)
type NestedURL struct {
URL string `mapstructure:"url"`
Nested int `mapstructure:"nested"`
}
type ZipCode struct {
Zipcode string `mapstructure:"zipcode"`
}
type Config struct {
Element [] map[string]NestedURL `mapstructure:"element"`
Weather [] map[string]ZipCode `mapstructure:"weather"`
}
func main() {
viper.SetConfigName("config")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
return
}
var config Config
if err := viper.Unmarshal(&config); err != nil {
fmt.Println(err)
return
}
fmt.Println(config)
}
config.yml
element:
- one:
url: http://test
nested: 123
- two:
url: http://test
nested: 123
weather:
- test:
zipcode: 12345
- ca:
zipcode: 90210
Upvotes: 14
Reputation: 1273
You can use Unmarshal
or UnmarshalKey
to parse all or part of your data and fill a struct. It is very similar to unmarshaling a json.
In your case, code will be like this:
package main
import (
"fmt"
"github.com/spf13/viper"
)
// here we define schema of data, just like what we might do when we parse json
type Element struct {
Url string `mapstructure:"url"`
Nested int `mapstructure:"nested"`
}
func main() {
viper.SetConfigName("config")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
// data in `element` key is a map of string to Element. We define a variable to store data into it.
elementParsed := make(map[string]*Element)
// read the key `element` in the yaml file, and parse it's data and put it in `elementParsed` variable
err = viper.UnmarshalKey("element", &elementParsed)
if err != nil {
panic(err)
}
fmt.Println(elementParsed["one"].Url) // will print: http://test
fmt.Println(elementParsed["one"].Nested) // will print: 123
}
Upvotes: 2
Reputation: 411
There are different Get methods available in viper library and your YML structure is of type []map[string]string
, so to parse your YML configuration file you have to use viper.Get
method. So you have to parse your file something like this..
Note: You can use struct as well to un-marshal the data. Please refer this post mapping-nested-config-yaml-to-struct
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigName("config")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
testvar := viper.Get("element")
fmt.Println(testvar)
elementsMap := testvar.([]interface{})
for k, vmap := range elementsMap {
fmt.Print("Key: ", k)
fmt.Println(" Value: ", vmap)
eachElementsMap := vmap.(map[interface{}]interface{})
for k, vEachValMap := range eachElementsMap {
fmt.Printf("%v: %v \n", k, vEachValMap)
vEachValDataMap := vEachValMap.(map[interface{}]interface{})
for k, v := range vEachValDataMap {
fmt.Printf("%v: %v \n", k, v)
}
}
}
}
// Output:
/*
Key: 0 Value: map[one:map[url:http://test nested:123]]
one: map[url:http://test nested:123]
url: http://test
nested: 123
Key: 1 Value: map[two:map[url:http://test nested:123]]
two: map[url:http://test nested:123]
url: http://test
nested: 123
*/
Upvotes: 6