$Id: 193

$SOId: 2503

YAML is a popular format for serializing data in a human friendly format. Think JSON but easier to read.

Thanks to its expressivness and readability, YAML is popular as a format for configuration files.

It’s also used in more complex scenarios like driving Ansible server automation.

There is no package in standard library for hanlding YAML format but there are community libraries including gopkg.in/yaml.v2.

Reading YAML file into a Go struct

dependencies:
  - name: apache
    version: 1.2.3
    repository: <http://example.com/charts>
  - name: mysql
    version: 3.2.1
    repository: <http://another.example.com/charts>
// no playground
package main

import (
	"fmt"
	"log"
	"os"

	"gopkg.in/yaml.v2"
)

// :show start
// Dependency describes a dependency
type Dependency struct {
	Name          string
	Version       string
	RepositoryURL string `yaml:"repository"`
}

type YAMLFile struct {
	Dependencies []Dependency `yaml:"dependencies"`
}

// :show end

func main() {
	// :show start
	f, err := os.Open("data.yml")
	if err != nil {
		log.Fatalf("os.Open() failed with '%s'\\n", err)
	}
	defer f.Close()

	dec := yaml.NewDecoder(f)

	var yamlFile YAMLFile
	err = dec.Decode(&yamlFile)
	if err != nil {
		log.Fatalf("dec.Decode() failed with '%s'\\n", err)
	}

	fmt.Printf("Decoded YAML dependencies: %#v\\n", yamlFile.Dependencies)
	// :show end
}

YAML decoding is very similar to JSON decoding.

If you know the structure of YAML file, you can define structs that mirror this structure and pass a pointer to a struct describing top-level structure to yaml.Decoder.Decode() function (or yaml.Unmarshal() if decoding from []byte slice).

YAML decoder does intelligent mapping between struct field names and names in YAML file so that e.g. name value in YAML is decoded into field Name in a struct.

It’s best to create explicit mappings using yaml struct tags. I only omitted them from the example to illustrate the behavior when they are not specified.

Writing Go struct to YAML file

// no playground
package main

import (
	"fmt"
	"log"

	yaml "gopkg.in/yaml.v2"
)

// :show start
type Person struct {
	fullName string
	Name     string
	Age      int    `yaml:"age"`
	City     string `yaml:"city"`
}

// :show end

func main() {
	// :show start
	p := Person{
		Name: "John",
		Age:  37,
		City: "SF",
	}
	d, err := yaml.Marshal(&p)
	if err != nil {
		log.Fatalf("yaml.Marshal failed with '%s'\\n", err)
	}
	fmt.Printf("Person in YAML:\\n%s\\n", string(d))
	// :show end
}

yaml.Marshal takes interface{} as an argument. You can pass any Go value, it’ll be wrapped into interface{} with their type.

Marshaller will use reflection to inspect passed value and encode it as YAML strings.

When serializing structs, only exported fields (whose names start with capital letter) are serialized / deserialized.