Easy (De)Serialisation of yaml files in Go

Marten Gartner
The Dev Project
Published in
3 min readMar 31, 2022

--

By using: gopkg.in/yaml.v3

Image from Shahadat Rahman via unsplash

Intro

Yaml, as a comparatively new format to save data, comes with wide adoption and easy syntax. It’s more human readable than e.g. JSON. Consequently, it is used as a configuration format in many modern open-source projects, like kubernetes or docker-compose. To work with yaml files, gopkg provides an awesome library called yaml.v3. Despite some other competing libraries, I really like the API yaml.v3 provides. Since I found the API description quite low-level, I decided to write about the general usage and some caveats to consider. So if you’re interested in diving into this, let’s get started!

Get Your Struct Ready

yaml.v3 works, as many other libraries, using reflection to inspect fields of a struct. It provides on tags, that are processed via tag reflection, to make configuration easy and in line with other libraries, so a struct can be transformed to json, yaml and others without changing it. A sample struct looks like this:

Compared to Name and Version, which are simply string fields without yaml tags, the Description field looks a bit different. Here I added two yaml tags. The first one is the representation within the yaml file, which is required if you want to add other tags. Per default, this is a lowercase version of the field name, therefor I named it accordingly. The second tag is omitempty, which is really useful to keep you yaml clean: Without it, empty strings (to which string fields are initialized per default in Go structs), will be serialized to “” in the yaml files. For larger structs, this looks really awkward. By adding omitempty, the field will not be serialized to yaml output and the final file looks cleaner.

So please keep the following two things in mind:

  • Use omitempty to avoid bloating your files with empty strings
  • You need to add the yaml field name as first tag if you want to use e.g. omitempty multiple times.

(De)Serialisation

After preparing the target struct to be ready to be transformed to yaml, the actual serialization and deserialization is the next step to look at. This can be done similarly to working with JSON by using marshal and unmarshal. The following gist provides an example for both ways.

That’s it. Using yaml.v3 is as easy as powerful. However, I want to add one caveat to consider.

Using Maps in yaml.v3

(De)serialising simple fields like strings or ints is straight forwarding. However, using complex types is sometimes not obvious. In my case, I wanted to add a slice of structs to my target struct and process it via yaml.v3. Adding them as slices didn’t work properly, therefor I tried using maps, which solved the issue. Looking at how my yaml file looks like, adding maps instead of slices makes sense. I have an example of maps of nested structs in the next gist:

Looking at the yaml output, it gets clear why to use maps instead of slices: We need a key for each item (in this case stack1 and stack2) to identify and further process it.

Summary

yaml.v3 did a very good job in my projects to save and load data from files in a human-readable format. It’s as easy to use as e.g. JSON, but much easier to read for humans. Only the usage of maps of nested structs got me a small headache. However, after understanding to use maps with string keys instead of slices, no further issues came along my way.

So I would definitely recommend the yaml.v3 library to everyone who wants to work with yaml in Go.

Cheers,
Marten!

--

--

Marten Gartner
The Dev Project

I’m a dad, software developer and researcher on network performance. Writing about high-speed frontend-dev, neworking, productivity and homeoffice with kids.