henrheid henrheid - 3 months ago 24
YAML Question

Go Unmarshal YAML into struct with maps

I'm attempting to unmarshal a YAML file into a struct containing two maps (using go-yaml).

YAML-file:

'Include':
- 'string1'
- 'string2'

'Exclude':
- 'string3'
- 'string4'


The struct:

type Paths struct {
Include map[string]struct{}
Exclude map[string]struct{}
}


A simplified version (i.e. removed error handling etc.) of the function attempting to unmarshal:

import "gopkg.in/yaml.v2"

func getYamlPaths(filename string) (Paths, error) {
loadedPaths := Paths{
Include: make(map[string]struct{}),
Exclude: make(map[string]struct{}),
}

filenameabs, _ := filepath.Abs(filename)
yamlFile, err := ioutil.ReadFile(filenameabs)

err = yaml.Unmarshal(yamlFile, &loadedPaths)
return loadedPaths, nil
}


Data is being read from the file, but the unmarshal-function is not putting anything into the struct, and is returning no errors.

I suspect that the unmarshal-function cannot turn the YAML collections into
map[string]struct{}
, but as mentioned it is producing no errors, and I've looked around for similar issues and can't seem to find any.

Any clues or insight would be much appreciated!

Answer

Through debugging I found multiple problems. First, yaml doesn't seem to care about the fields names. You have to annotate the fields with a

`yaml:"NAME"`

Second, in the YAML file, Include and Exclude both contain only a list of strings, not something similar to a map. So your struct becomes:

type Paths struct {
    Include []string `yaml:"Include"`
    Exclude []string `yaml:"Exclude"`
}

And it works. Full code:

package main

import (
    "fmt"
    "gopkg.in/yaml.v2"
)

var str string = `
'Include':
    - 'string1'
    - 'string2'

'Exclude':
    - 'string3'
    - 'string4'
`

type Paths struct {
    Include []string `yaml:"Include"`
    Exclude []string `yaml:"Exclude"`
}

func main() {
    paths := Paths{}

    err := yaml.Unmarshal([]byte(str), &paths)

    fmt.Printf("%v\n", err)
    fmt.Printf("%+v\n", paths)
}