Try it yourself with our free Json Yaml Converter tool — runs entirely in your browser, no signup needed.

How to Parse YAML in Go

How to Parse YAML in Go

YAML (YAML Ain't Markup Language) is a human-readable data serialization format commonly used for configuration files, data exchange, and debugging. In Go, parsing YAML is a crucial task when working with configuration files, API responses, or data storage. In this guide, we will explore how to parse YAML in Go, covering the basics, edge cases, common mistakes, and performance tips.

Quick Example

Here's a minimal example of parsing YAML in Go:

package main

import (
	"fmt"
	"gopkg.in/yaml.v3"
)

type Config struct {
	Server   string `yaml:"server"`
	Port     int    `yaml:"port"`
	Username string `yaml:"username"`
}

func main() {
	yamlData := `
server: localhost
port: 8080
username: admin
`

	var config Config
	err := yaml.Unmarshal([]byte(yamlData), &config)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("Server: %s, Port: %d, Username: %s\n", config.Server, config.Port, config.Username)
}

To use this code, install the gopkg.in/yaml.v3 package using the following command:

go get gopkg.in/yaml.v3

This example demonstrates how to unmarshal YAML data into a Go struct.

Step-by-Step Breakdown

Let's walk through the code:

  1. We import the yaml package from gopkg.in/yaml.v3.
  2. We define a Config struct with fields corresponding to the YAML data.
  3. In the main function, we define a YAML string containing the data to be parsed.
  4. We create a Config struct instance and pass its address to the yaml.Unmarshal function along with the YAML data.
  5. If the unmarshaling process fails, we print the error and return.
  6. Finally, we print the parsed data using the Config struct fields.

Handling Edge Cases

Empty/Null Input

When dealing with empty or null input, the yaml.Unmarshal function will return an error. You can handle this case by checking the error value:

yamlData := ""
var config Config
err := yaml.Unmarshal([]byte(yamlData), &config)
if err != nil {
	fmt.Println(err) // Output: yaml: unmarshal errors:
}

Invalid Input

Invalid YAML input will also result in an error. You can handle this case similarly:

yamlData := " invalid yaml"
var config Config
err := yaml.Unmarshal([]byte(yamlData), &config)
if err != nil {
	fmt.Println(err) // Output: yaml: unmarshal errors:
}

Large Input

When dealing with large YAML input, you may encounter performance issues. To mitigate this, consider using a streaming parser or processing the data in chunks.

Unicode/Special Characters

YAML supports Unicode characters, but you may need to handle special characters explicitly. For example, to handle non-ASCII characters, use the yaml.Tag field:

type Config struct {
	Server   string `yaml:"server"`
	Port     int    `yaml:"port"`
	Username string `yaml:"username"`
}

yamlData := `
server: 
port: 8080
username: 
`

var config Config
err := yaml.Unmarshal([]byte(yamlData), &config)
if err != nil {
	fmt.Println(err)
}

Common Mistakes

1. Incorrect Struct Field Tags

Make sure to use the correct YAML tags for your struct fields:

// Wrong
type Config struct {
	Server string
	Port   int
	Username string
}

// Correct
type Config struct {
	Server   string `yaml:"server"`
	Port     int    `yaml:"port"`
	Username string `yaml:"username"`
}

2. Forgetting to Handle Errors

Always check the error value returned by yaml.Unmarshal:

// Wrong
yaml.Unmarshal([]byte(yamlData), &config)

// Correct
err := yaml.Unmarshal([]byte(yamlData), &config)
if err != nil {
	fmt.Println(err)
}

3. Not Using the Correct YAML Package

Use the gopkg.in/yaml.v3 package instead of the outdated gopkg.in/yaml.v2 package:

// Wrong
import "gopkg.in/yaml.v2"

// Correct
import "gopkg.in/yaml.v3"

Performance Tips

1. Use Streaming Parsing

For large YAML input, consider using a streaming parser to process the data in chunks:

decoder := yaml.NewDecoder(strings.NewReader(yamlData))
for {
	var config Config
	err := decoder.Decode(&config)
	if err != nil {
		break
	}
	fmt.Printf("Server: %s, Port: %d, Username: %s\n", config.Server, config.Port, config.Username)
}

2. Avoid Reflecting on Struct Fields

When possible, avoid using reflection to access struct fields, as it can impact performance:

// Slow
for _, field := range reflect.ValueOf(config).Fields() {
	fmt.Println(field.Name)
}

// Fast
fmt.Println(config.Server)

3. Use a Buffer for Large Input

When dealing with large YAML input, use a buffer to reduce memory allocation:

buf := make([]byte, 1024)
n, err := yamlData.Read(buf)
if err != nil {
	fmt.Println(err)
}

FAQ

Q: What is the difference between yaml.Unmarshal and yaml.UnmarshalStrict?

A: yaml.UnmarshalStrict is a more strict version of yaml.Unmarshal that returns an error if the input YAML is not valid.

Q: How do I handle YAML anchors and aliases?

A: You can use the yaml.Tag field to handle YAML anchors and aliases.

Q: Can I use YAML with Go's encoding/json package?

A: No, the encoding/json package does not support YAML. Use the gopkg.in/yaml.v3 package instead.

Q: How do I parse YAML from a file?

A: Use the os package to read the file contents and then pass it to yaml.Unmarshal.

Q: Can I use YAML with Go's encoding/xml package?

A: No, the encoding/xml package does not support YAML. Use the gopkg.in/yaml.v3 package instead.

AI agent tools available. The CodeTidy MCP Server gives Claude, Cursor, and other AI agents access to 60+ developer tools. One command: npx @codetidy/mcp