Reading a slice of maps with Golang Viper

go
13,307

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".

Share:
13,307

Related videos on Youtube

jaxxstorm
Author by

jaxxstorm

Updated on September 14, 2022

Comments

  • jaxxstorm
    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 standard viper.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
    jaxxstorm over 7 years
    Once it's been Unmarshalled, how are the values then made available?
  • Uvelichitel
    Uvelichitel over 7 years
    values 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
    jaxxstorm over 7 years
    I 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?