Reading a slice of maps with Golang Viper
Solution 1
Instead of call raw Get and then decide what exactly you get I'd suggest first describe your desired config structure, something like
type config struct {
interval int `mapstructure:"Interval"`
statsdPrefix string `mapstructure:"statsd_prefix"`
groups []group
}
type group struct {
group string `mapstructure:"group"`
targetPrefix string `mapstructure:"target_prefix"`
targets []target
}
type target struct {
target string `mapstructure:"target"`
hosts []string `mapstructure:"hosts"`
}
and than unmarshall(decode) in it
var C config
err := viper.Unmarshal(&C)
if err != nil {
t.Fatalf("unable to decode into struct, %v", err)
}
assuming decoder is smart enough to unmurshall in provided structure if it possible and meaningful.
Solution 2
The question is a bit older, but I figured since I stumbled upon this, somebody else might come here looking...
The primary issue in the original question is that this JSON notation defines a list, not a map:
hosts = [
"dnsserver1",
"dnsserver2"
]
So the simple solution in this case is not to use viper.GetStringMap("group")
, but instead viper.GetStringSlice("group")
.
A slice in GO is what most other programming languages refer to as an Array or a List.
So in this case, the Slice would then be ["dnsserver1", "dnsserver2"]
.
If you wanted to properly use the GetStringMap()
function of viper, you need a different format in your config file, namely a map format:
"hosts": {
"dnsserver1": "8.8.8.8",
"dnsserver2": "8.8.4.4"
}
This can then be parsed using viper.GetStringMap()
into a map[string]string
Structure, which looks like this: map["dnsserver1":"8.8.8.8" "dnsserver2":"8.8.4.4"]
.
You could then access the map at a given key, like map["dnsserver1"]
which would give you a string containing "8.8.8.8".
Related videos on Youtube
jaxxstorm
Updated on September 14, 2022Comments
-
jaxxstorm over 1 year
I'm using the excellent viper library from here: https://github.com/spf13/viper
I'm trying to read in a config file in hcl (although it could be a JSOn or YAML file as well) which looks like this:
interval = 10 statsd_prefix = "pinger" group "dns" { target_prefix = "ping" target "dns" { hosts = [ "dnsserver1", "dnsserver2" ] } }
The code I have so far looks like this:
viper.SetConfigName("config") viper.AddConfigPath(".") err := viper.ReadInConfig() if err != nil { panic(fmt.Errorf("Fatal error config file: %s \n", err)) } interval := viper.GetInt("interval") prefix := viper.GetString("statsd_prefix") groups := viper.GetStringMap("group") fmt.Println(interval) fmt.Println(prefix)
The big problem I'm having is with the group option. This can be multiple different groups.
It doesn't seem to work when I read it in using
viper.GetStringMap
, so I used the standardviper.Get
function. The resulting structure looks like this when dumped:([]map[string]interface {}) (len=1 cap=1) { (map[string]interface {}) (len=1) { (string) (len=3) "dns": ([]map[string]interface {}) (len=1 cap=2) { (map[string]interface {}) (len=2) { (string) (len=13) "target_prefix": (string) (len=4) "ping", (string) (len=6) "target": ([]map[string]interface {}) (len=1 cap=1) { (map[string]interface {}) (len=1) { (string) (len=8) "dns": ([]map[string]interface {}) (len=1 cap=1) { (map[string]interface {}) (len=1) { (string) (len=5) "hosts": ([]interface {}) (len=2 cap=2) { (string) (len=18) "dnsserver1", (string) (len=18) "dnsserver2" } } } } } } } } }
It seems to be of type slice when I use reflect. Do I need to cast it to a slice? How do I do that? Is there an easier way of managing a data structure like this?
I'm completely new to golang, so please go easy on me :)
-
jaxxstorm over 7 yearsOnce it's been Unmarshalled, how are the values then made available?
-
Uvelichitel over 7 yearsvalues can be available as interv:=C.interval or in loop for grp:=range C.groups{ for trg:=range grp{...}} Error is most likely cause of a typo-change all grope to group sure.
-
jaxxstorm over 7 yearsI don't think it is a typo, it happens regardless of the name of the next struct. I think this is because all this code is in main.go?