Accessing Nested Config with Viper

I'm writing a Go application that glues together a bunch of other things, so it has a whole bunch of basically unrelated config, dumped in a yaml file. I was struggling a little with this non-standard use of Viper but actually, it does everything I needed. And, presumaby, more besides. I thought I would put some examples here to show how to handle this.

A Slice of Structs

Let's start with the configured colours to send to my LED shelf (which I realised I didn't blog about, but I should!) In the config.yaml file it is a series of objects, with properties red, green and blue:

shelf_lights:
  - red: 120
    blue: 200
  - red: 120
    green: 150
  - blue: 200
    green: 40
    red: 60

In my code, I have a struct called LEDColour to represent each of these objects, and it uses field tags to explain how the yaml data fits into the struct - similar to how the JSON field tags work if you've seen those. Viper uses a neat package called mapstructure to handle this part. My struct looks like this:

type LEDColour struct {
	Red   uint8 `mapstructure:"red"`
	Green uint8 `mapstructure:"green"`
	Blue  uint8 `mapstructure:"blue"`
}

To wrangle the yaml data into this structure, I declare the variable it will go into, then Viper has an UnmarshalKey which allows me to grab just the shelf_lights section from config and work on that.

	var lights []LEDColour
	viper.UnmarshalKey("shelf_lights", &lights)

There was a lot of preamble but the actual moment it happens is neat!

Map of Structs with String Keys

This is pretty similar to above but I think a specific example might help. With a section of the yaml config file like this:

obs_scenes:
  Camera:
    name: Camera 
    image: "/camera.png"
  Offline:
    name: Offline
    image: "/offline.png"
  Secrets:
    name: Secrets
    image: "/secrets.png"

I have another struct, also with mapstructure features:

type ObsScene struct {
	Name     string `mapstructure:"name"`
	Image    string `mapstructure:"image"`
	ButtonId int
}

The ButtonId field isn't updated until the scene is assigned to a button dynamically (this is from my streamdeck utility, OBS is a video streaming tool, don't worry if the words don't mean anything! One OBS has many scenes) so we don't need to include it when we're reading from config. Which looks like this:

var buttons_obs map[string]*ObsScene
buttons_obs = make(map[string]*ObsScene)
viper.UnmarshalKey("obs_scenes", &buttons_obs)

As usual, Viper has us covered and while for many applications it makes sense to read in the whole config file and refer to each setting as you need it, this ability to transform sections of config is very handy indeed!


Also published on Medium.

Leave a Reply

Please use [code] and [/code] around any source code you wish to share.

This site uses Akismet to reduce spam. Learn how your comment data is processed.