# goccy/go-yaml: a YAML library built to replace gopkg.in/yaml

> goccy/go-yaml is a from scratch YAML implementation for Go with no dependencies, an error format meant for humans, YAML Path editing, and a tokenizer and parser you can use directly.

**goccy/go-yaml** — YAML support for the Go language

- Repository: https://github.com/goccy/go-yaml
- Website: https://goccy.github.io/go-yaml
- Stars: 2,232 · Forks: 268
- Language: Go
- License: MIT
- Published: 2026-10-06 · Updated: 2026-10-06 · Language: en
- Canonical page: https://hysenlabs.com/projects/goccy-go-yaml

## Why the author threw out the libyaml port

The README opens with a warning that will stop you importing the wrong package: this library has no relation to the go-yaml/yaml library. The import path is `github.com/goccy/go-yaml`, while the older one is `gopkg.in/yaml.v3`, and the two are frequently confused because the module name contains `go-yaml`.

The stated reasons for writing a replacement are specific rather than aesthetic. `go-yaml/yaml` is described as not actively maintained. It was a port of the C libyaml, so the source is not written in Go style. There is content it cannot parse. Its errors are not intuitive, which matters because the README argues YAML is mostly used for configuration where validation errors have to mean something to a human. And for tools that transform YAML, manipulating the AST is described as the only option when the document contains comments or anchors and aliases, which breaks reversible transformation.

The transitive reach of that critique is worth noting. The README points out that libraries such as ghodss/yaml and sigs.k8s.io/yaml depend on go-yaml/yaml, so if you use those you inherit both the parsing gaps and the error messages, even though your own code never imports it.

## A dependency free install and an interface you already know

The install is one line:

```sh
go get github.com/goccy/go-yaml
```

No dependencies means the module file has nothing after the module declaration, which is confirmed by the repository's own `go.mod`:

```go
module github.com/goccy/go-yaml

go 1.21.0
```

The basic API deliberately mirrors the package it replaces, so migration is mostly a change of import path. The README's first example builds an anonymous struct, marshals it with reflection, and prints the result:

```go
var v struct {
	A int
	B string
}
v.A = 1
v.B = "hello"
bytes, err := yaml.Marshal(v)
if err != nil {
	//...
}
fmt.Println(string(bytes)) // "a: 1\nb: hello\n"
```

The comment on the last line shows the exact output including the trailing newline, which is the kind of detail that saves time when a golden file test disagrees with you.

## Struct tags, the json tag, and the precedence between them

Field mapping follows the familiar `yaml` tag, and unmarshalling is the same shape as marshalling:

```go
yml := `---
foo: 1
bar: c
`
var v struct {
	A int    `yaml:"foo"`
	B string `yaml:"bar"`
}
if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
	//...
}
```

Then there is the compatibility detail that matters most in practice. The library also accepts the `json` tag, and the README is precise about two limits: not every option in a `json` tag has significance when parsing YAML, and when both tags are present the `yaml` tag wins. So a struct already carrying json tags will mostly work as is, and a struct that needs both is unambiguous.

Beyond tags, the library honours `encoding/json` shaped behaviour. It supports `json.Marshaler` style marshallers and `json.Unmarshaler` style unmarshallers, with `UseJSONMarshaler` and `UseJSONUnmarshaler` as the switches, plus `RegisterCustomMarshaler` and `RegisterCustomUnmarshaler` for primitive and third party types. The README describes the result as respecting encoding/json behaviour, and the practical upshot is that a type that already round trips through JSON usually needs no changes here.

For custom types there are two flavours of the interface and the choice is about performance. The `BytesMarshaler` and `BytesUnmarshaler` pair behaves like `encoding/json`, while `InterfaceMarshaler` and `InterfaceUnmarshaler` behaves like `gopkg.in/yaml.v2`. The README notes the two are semantically the same but differ in cost, because indentation matters in YAML and a marshaller cannot simply accept any valid YAML fragment.

## YAML Path editing and reversible transformation without the AST

This is the part of the feature list that has no equivalent in the library it replaces. go-yaml can filter, replace and merge YAML content addressed by YAML Path, and it can do reversible transformation of documents containing anchors, aliases and comments without going through an AST rewrite. That is exactly the gap the README identifies: reversible transformation of commented or anchored YAML normally forces you to manipulate the AST.

The `path.go` and `path_test.go` files at the top of the tree are where that lives, and `printer/` is what makes the transformation readable rather than a reformatting accident. Related to it is the ability to reference elements declared in another file via anchors, which matters for Kubernetes style setups where one file defines a fragment and another refers to it.

You can also go lower than the document level. The `lexer/` and `parser/` packages are exported, with `lexer.Tokenize` and `parser.Parse` named in the README as public entry points, alongside `ast/`, `scanner/`, `token/` and `internal/`. That is an unusual shape for a YAML library: instead of only offering Encode and Decode, it publishes the layers, which is what lets a tool build a different tool.

## Validation, error formatting and a playground for bug reports

YAML without validation tends to fail late, so the library integrates with go-playground/validator and calls the combination smart validation processing. There is an example test file referenced in the README for exactly this. The integration is visible in the tree as `validate.go`, and it is configurable rather than forced on you.

Error output is listed as a pretty format for error notifications, and this is the feature the whole project exists for. The related release history shows the feature set maturing at the edges: v1.19.1 fixed decoding of integer keys on map types and added line comments for flow sequences and flow maps, while v1.19.0 normalized CR and CRLF inside multi-line strings, supported non string map keys, added support for RawMessage in the style of `json.RawMessage`, and allowed a decode option to limit which field prefixes are accepted.

Coverage claims are stated with a specific date attached. The README says the YAML Test Suite contains 402 cases, of which `gopkg.in/yaml.v3` passes 295, and that goccy/go-yaml passes nearly 60 additional cases, as of 2024-12-15. The test code is linked at `yaml_test_suite_test.go` in the repository, so the claim is checkable rather than decorative. The repository also carries a `fuzz_test.go` and a `benchmarks/` directory, and the homepage doubles as a playground that visualizes how the library processes a YAML document, which is the fastest way to explain a parse failure to someone else.

## Conclusion

goccy/go-yaml is a reasonable default choice for a new Go service that reads YAML configuration, because it has no dependencies, its errors point at a source line, and it passes roughly 60 YAML Test Suite cases that gopkg.in/yaml.v3 fails. The reason it exists is the reason to be careful with it: it is a competing implementation, and the ecosystems around ghodss/yaml and sigs.k8s.io/yaml still sit on gopkg.in/yaml, so anything you pull in transitively may parse with different rules. Reach for it deliberately rather than by accident. Start at v1.19.2 from 2026-01-08, try your actual configuration files against it before switching, and keep the yaml tag in preference to the json tag in mind when you write structs, because that precedence is what lets a file round trip through the encoding/json shaped marshaler interfaces.

## FAQ

### What is the difference between goccy/go-yaml and gopkg.in/yaml?

They are unrelated implementations with confusingly similar names. goccy/go-yaml was written from scratch to replace gopkg.in/yaml, on the grounds that the older library is not actively maintained, was a port of C libyaml, cannot parse some valid documents, and produces errors that are hard to act on when validating configuration.

### Does goccy/go-yaml have any dependencies?

No. The README lists no dependencies as a feature, and the repository go.mod contains only the module declaration and a Go 1.21.0 language version. Validation with go-playground/validator is offered as an integration rather than a required dependency.

### Can goccy/go-yaml read struct tags written for encoding/json?

Yes, with limits. The README says the library accepts the json tag, that not all json tag options have significance when parsing YAML, and that the yaml tag takes precedence if both are present. It also supports the encoding/json marshaler and unmarshaler interfaces, with UseJSONMarshaler and UseJSONUnmarshaler options.

### How do I filter or merge part of a YAML document?

The library exposes YAML Path operations for filtering, replacing and merging content, which the repository implements in path.go. It can also do reversible transformation of documents containing anchors, aliases and comments without manipulating the AST, a case the README says go-yaml/yaml cannot handle.

## Sources

- [goccy/go-yaml on GitHub](https://github.com/goccy/go-yaml)
- [License: MIT](https://github.com/goccy/go-yaml/blob/master/LICENSE)
- [Project website](https://goccy.github.io/go-yaml)
- [README](https://github.com/goccy/go-yaml/blob/master/README.md)
- [Releases](https://github.com/goccy/go-yaml/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/goccy-go-yaml
