noranraskin
noranraskin

Reputation: 1417

Go unmarshal yaml and preserve order

Let's say I have the following yaml file:

key1:
    - "Value1"
    - "Value2"
key2:
    - "Value3"
    - "Value4"

And I want to unmarshal that in Go. I can let Go decide what the scheme should look like and print it out.

m := make(map[interface{}]interface{})
err := yaml.Unmarshal(yaml_file, &m)
fmt.Prinf(m)

This will print something like this: map[key1:[Value1 Value2] key2:[Value3 Value4]]

But maps are unordered. The order is very important for me tho and I also don't want to loose the keys.

I can't just create a struct and try to unmarshal the yaml file to a slice of that struct...

type Element struct {
    Key   string
    Value interface{}
}

t := []Element{}
err := yaml.Unmarshal(yaml_file, &t)
if err != nil {
    log.Fatalf("error: %v", err)
}

Will print error: yaml: unmarshal errors: line 2: cannot unmarshal !!map into []Element

There is no field tag to tell the yaml parser to fill the Key property with the yaml key.

Is there a different way than writing my own decoder?

Upvotes: 3

Views: 4797

Answers (1)

noranraskin
noranraskin

Reputation: 1417

Just found the answer right before I wanted to post the question.

The answer is MapSlice! You need to import yaml.v2 from gopkg. (As of writing this MapSlices don't seem to be implemented in yaml.v3 yet)

MapSlices implement exactly what I needed and are made of a number of MapItems.

type MapItem struct {
    Key, Value interface{}
}

So now just unmarshal to a MapSlice. If you want to work with your own types you can always convert it but might need to do some typecasting.

m := yaml.MapSlice{}
yaml.Unmarshal(yaml_file, &m)

t := []Element{}
for _, item := range m {
    t = append(t, Element{item.Key.(string), item.Value})
}

Upvotes: 6

Related Questions