Working with YAML in Go

12.06.2021

Intro

YAML is another domain specific language (DSL) that allows you to defined data. It is an alternative to JSON and is used heavily in apps such as Kubernetes. In this article, we will learn how to use YAML in Go.

Getting Started

To begin, we need to create main.go and create a Go module.

go mod init yamlexample

Now, we will first need to install a yaml package.

go get gopkg.in/yaml.v3

Let's then create an example file called example.yaml and add the following.

first_name: Jan
last_name: Audrey

The basic flow is to read the file, the use the Unmarshal function to map our data to a Go map. This is very similar to other languages like JSON if you are familiar.

Now add some imports to our file.

import (
	"fmt"
	"io/ioutil"
	"log"

	"gopkg.in/yaml.v3"
)

First, let's read the file

file, err := ioutil.ReadFile("example.yaml")

if err != nil {
	log.Fatal(err)
}

Then, let's create a generic map using interface to match anything.

data := make(map[interface{}]interface{})

err2 := yaml.Unmarshal(file, &data)

if err2 != nil {
	log.Fatal(err2)
}

We can then print our data.

for key, value := range data {
    fmt.Println(key, value)
}

Objects YAML reading

Above we mapped data to interfaces. We can map yaml to more specific interfaces as well. Let's start with a new yaml file with a list of employees.

employee1:
    name: Jan
    salary: 90000
employee2:
    name: Mary
    occupation: 60000

Now, let's create a struct to hold our employee data. Copy the following to employees.yaml.

type Employee struct {
	Name string
	Salary int
}

Next let's read the file.

file, err := ioutil.ReadFile("employees.yaml")

if err != nil {
	log.Fatal(err)
}

Then, we can map our data using our structure.

data := make(map[string]Employee)

err2 := yaml.Unmarshal(file, &data)

if err2 != nil {
	log.Fatal(err2)
}

Writing to Files

Let's end our yaml tutorial by learning to write data. We can do this using the Marshal function which does the opposite of above.

users := map[string]Employee{
	"employee1": {"Jan", 90000},
	"employee2": {"Mary", 60000},
}

data, err := yaml.Marshal(&users)

if err != nil {

	log.Fatal(err)
}

err2 := ioutil.WriteFile("users.yaml", data, 0)

if err2 != nil {

	log.Fatal(err2)
}

The Full Example

Here is the full example code. You can run this by copying the contents into a file called main.go and then running go run main.go.

package main

import (
	"fmt"
	"io/ioutil"
	"log"

	"gopkg.in/yaml.v3"
)

func example1() {
	file, err := ioutil.ReadFile("example.yaml")

	if err != nil {
		log.Fatal(err)
	}

	data := make(map[interface{}]interface{})

	err2 := yaml.Unmarshal(file, &data)

	if err2 != nil {
		log.Fatal(err2)
	}

	for key, value := range data {
		fmt.Println(key, value)
	}
}

type Employee struct {
	Name   string
	Salary int
}

func example2() {
	file, err := ioutil.ReadFile("employees.yaml")

	if err != nil {
		log.Fatal(err)
	}

	data := make(map[string]Employee)

	err2 := yaml.Unmarshal(file, &data)

	if err2 != nil {
		log.Fatal(err2)
	}
}

func example3() {
	users := map[string]Employee{
		"employee1": {"Jan", 90000},
		"employee2": {"Mary", 60000},
	}

	data, err := yaml.Marshal(&users)

	if err != nil {

		log.Fatal(err)
	}

	err2 := ioutil.WriteFile("users.yaml", data, 0)

	if err2 != nil {

		log.Fatal(err2)
	}
}

func main() {
	example1()
	example2()
	example3()
}