3 reasons to use github.com/goccy/go-yaml to handle YAML in Go

Daisuke Maki
2 min readOct 28, 2019

--

To be very honest, I’ve never been a big fan of the defacto standard in YAML handling in Go, gopkg.in/yaml/yaml.v2 . But I think I can finally support YAML in my tools thanks to github.com/goccy/go-yaml

1. The API

For starters, I have no qualm about gopkg.in/yaml/yaml.v2 ‘s features and performance. It does the job well. The problem I had mainly arose from the fact that I wanted to use YAML and JSON interchangeably.

For example, supporting both YAML and JSON as config file format was one of the things I wanted to do. What I really wanted to do was:

var unmarshaler func([]byte, interface{}) error
if format == "yaml" {
unmarshaler = yaml.Unmarshal
} else {
unmarshaler = json.Unmarshal
}
unmarshaler(data)

This is unfortunately not the case with gopkg.in/yaml/yaml.v2 because its Marshaler and Unmarshaler interface reads as follows, and the function signatures do not match.

type Unmarshaler interface {
UnmarshalYAML(unmarshal func(interface{}) error) error
}
type Marshaler interface {
MarshalYAML() (interface{}, error)
}

In their defense, this makes sense, because YAML is very much context-aware — i.e. indents matter.

For example, when you marshal a piece of data into YAML, you cannot just concatenate the serialized format to other pieces of YAML like in JSON. In order to properly format the YAML data, the serializer needs to re-encode the data, which adds much overhead. Similarly, for unmarshal, you need to know the underlying context to properly parse the data.

github.com/goccy/go-yaml solves this problem by supporting BOTH APIs: one that looks like "encoding/json".Marshal and one that looks like "gopkg.in/yaml/yaml.v2".Marshal . You can use either, but you just have to know the tradeoffs.

2. Error Reporting

When parsing YAML, github.com/goccy/go-yaml can optionally display the error context (where in the YAML data the error) and to colorize the output.

Given a code like this:

const src = `---
a:
- b
c: d
`
var v struct {
A []string
}
err := yaml.NewDecoder(strings.NewReader(src)).Decode(&v)
if err == nil {
t.Fatalf("expected error")
}
t.Logf("%s", err)

You get an output like this:

--- PASS: TestDecoder_InvalidCases (0.00s)
decode_test.go:375: [3:3] unexpected key name
1 | ---
2 | a:
> 3 | - b
4 | c: d
^

By default colorization is disabled, and displaying the context is enabled. If you want to have more control over how the error is displayed, you can use yaml.FormatError

yaml.FormatError(error, colorize, includeSource) string

This is such a cool feature. I love it.

3. Anchors and Aliases

I personally don’t use anchors and aliases in my YAML docs, but as far as I know gopkg.in/yaml/yaml.v2 only parses documents with anchors and liases, and does no support creating documents with them.

This is done by actually calculating the aliases elements from their pointer addresses. If you assign values from the same address, they will be properly anchored and aliased!

I helped with a bit of documentation patches and what not, but kudos to @goccy54 for writing this tool! Meanwhile, I need to decide if I should support YAML config files for peco

--

--

Daisuke Maki
Daisuke Maki

Written by Daisuke Maki

Go/perl hacker; author of peco; works @ Mercari; ex-mastermind of builderscon; Proud father of three boys;

Responses (1)