How do I use Viper to get a value from a nested YAML structure?

Defenestrator6 picture Defenestrator6 · Oct 1, 2018 · Viewed 9.2k times · Source

My question:

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 new to go so not sure how to make one, especially if it needs to be nested.

Answer

Deepak Singh picture Deepak Singh · Oct 1, 2018

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
*/