In this tutorial, we will walk through how to work with yaml
file in Golang. We will cover the following basic areas of YAML parsing:
- Parse YAML from a variable or file into struct
- Parse YAML from a variable or file into map (without using struct)
- Access individual nested elements from YAML file as part of map or structs
Golang parse YAML into struct
In golang we can use the gopkg.in/yaml.v3
package to parse YAML data into a struct. We will need to define a struct that matches the structure of the YAML data. Then we can use the yaml.Unmarshal
function to parse the YAML data into an instance of that struct.
The import path for the package is gopkg.in/yaml.v3.
To install it, run:
$ go get gopkg.in/yaml.v3
go: downloading gopkg.in/yaml.v3 v3.0.1
go get: added gopkg.in/yaml.v3 v3.0.1
Example-1: Parse a simple YAML File into struct
Here is a simple YAML file that has two fields: name
and age
, both are strings which we will parse into a struct:
name: Amit Kumar
age: 35
Here is a sample code to parse the YAML file and return the content into pre-defined struct:
package main
import (
"fmt"
"io/ioutil"
"gopkg.in/yaml.v3"
)
type Config struct {
Name string `yaml:"name"`
Age int `yaml:"age"`
}
func main() {
// Read the file
data, err := ioutil.ReadFile("config.yaml")
if err != nil {
fmt.Println(err)
return
}
// Create a struct to hold the YAML data
var config Config
// Unmarshal the YAML data into the struct
err = yaml.Unmarshal(data, &config)
if err != nil {
fmt.Println(err)
return
}
// Print the data
fmt.Println(config)
}
In this example, the struct Config
is defined with fields that match the structure of the YAML data. The yaml struct tags are used to indicate the corresponding field names in YAML.
Then, the ioutil.ReadFile
function is used to read the contents of the file "config.yaml
" into the data
variable. Then the yaml.Unmarshal
function is used to parse the YAML data into an instance of struct Config
.
Keep in mind that, if the structure of the YAML is not known and we want to handle all types of YAML then it is recommended to use map[interface{}]interface{}
instead of struct which we will cover in next examples.
Output:
# go run main.go
{Amit Kumar 35}
Example-2: Parse nested YAML file into struct
In this example we will parse a more complex nested YAML structure. Here is our sample YAML file config.yaml
which we will use in our example code:
name: Amit Kumar
age: 35
address:
street: 123 Some Random Street
city: Chennai
state: TN
zip: 763098
phoneNumbers:
- type: home
number: 0123456789
- type: work
number: 0987654321
The below example golang code defines a struct Person
with fields that match the structure of the YAML data. The yaml struct tags are used to indicate the corresponding field names in YAML.
In this example, the Person
struct contains nested structs: Address
and PhoneNumber
and also a slice of PhoneNumber
structs, which is used to handle the array of phone numbers in the YAML file.
package main
import (
"fmt"
"io/ioutil"
"gopkg.in/yaml.v3"
)
type Address struct {
Street string `yaml:"street"`
City string `yaml:"city"`
State string `yaml:"state"`
Zip string `yaml:"zip"`
}
type PhoneNumber struct {
Type string `yaml:"type"`
Number string `yaml:"number"`
}
type Person struct {
Name string `yaml:"name"`
Age int `yaml:"age"`
Address Address `yaml:"address"`
PhoneNumber []PhoneNumber `yaml:"phoneNumbers"`
}
func main() {
// Read the file
data, err := ioutil.ReadFile("config.yaml")
if err != nil {
fmt.Println(err)
return
}
// Create a struct to hold the YAML data
var person Person
// Unmarshal the YAML data into the struct
err = yaml.Unmarshal(data, &person)
if err != nil {
fmt.Println(err)
return
}
// Print the data
fmt.Println(person)
}
In the main function, we use the ioutil.ReadFile
function to read the contents of the file "config.yaml
" into the data
variable. Then the yaml.Unmarshal
function is used to parse the YAML data into an instance of struct Person
.
Finally, we print the struct which contains the parsed data from the yaml file.
Output:
# go run main.go
{Amit Kumar 35 {123 Some Random Street Chennai TN 763098} [{home 0123456789} {work 0987654321}]}
Example-3: Access individual fields of YAML File using Structs
Once we have parsed a YAML file into a struct then we can access individual fields or elements of the data by referencing them by their keys as shown below.
fmt.Println(person.Name) // Amit Kumar
In case of nested fields in struct we can access them using the dot notation. Here's an example of how we can access a nested field in a struct:
fmt.Println(person.Address.City) // Chennai
The phone numbers in our YAML file are defined as slice of PhoneNumber
structs, which is the field PhoneNumber
in the Person
struct.
for _, phone := range person.PhoneNumber {
if phone.Type == "home" {
fmt.Println(phone.Number) // 0123456789
}
}
Here the range
keyword is used to iterate over the slice and it returns two values: an index and a copy of the element at that index. In this case, we are not using the index, so we use the blank identifier _
to ignore it. For each element in the slice, it checks the value of the Type
field using an if
statement. If the value is "home
", it prints the value of the Number
field using fmt.Println
Alternatively we can use a for loop with index and check the Type
field of the element in the PhoneNumber
slice.
for i := 0; i < len(person.PhoneNumber); i++ {
if person.PhoneNumber[i].Type == "work" {
fmt.Println(person.PhoneNumber[i].Number) // 0987654321
}
}
Here we use a for loop with index to iterate over the slice of PhoneNumber
structs. The for loop starts with an initializer i:=0
, a condition i < len(person.PhoneNumber)
and an increment i++
. Inside the loop, it checks the Type
field of the element in the PhoneNumber
slice using an if statement. If the value is "home
", it prints the value of the Number field using fmt.Println
Golang parse YAML into Map (Without using struct)
Example-1: Parse YAML variable into map
In this example we parse YAML stored in a variable into map.
package main
import (
"fmt"
"gopkg.in/yaml.v3"
)
func main() {
// YAML string stored in a variable
yamlString := `
name: Amit Kumar
age: 34
address:
city: Chennai
state: TN
`
// Map to store the parsed YAML data
var data map[string]interface{}
// Unmarshal the YAML string into the data map
err := yaml.Unmarshal([]byte(yamlString), &data)
if err != nil {
fmt.Println(err)
}
// Access data in the map
fmt.Println("Name:", data["name"])
fmt.Println("Age:", data["age"])
fmt.Println("City:", data["address"].(map[string]interface{})["city"])
}
In this example, we first define a variable yamlString
which contains a YAML string. Then we define an empty map data that will store the parsed YAML data.
We then use the yaml.Unmarshal
function to parse the YAML string stored in the variable yamlString
, and store the result in the data map. The Unmarshal
function takes two arguments: the YAML data and a pointer to the variable that will store the result.
# go run main.go
Name: Amit Kumar
Age: 34
City: Chennai
Example-2: Parse YAML file into map
In this example, we will parse a more complex yaml (config.yaml
) file. In this example, we define a complex struct instead of simple map to store the parsed data.
name: Amit Kumar
age: 35
address:
street: 123 Some Random Street
city: Chennai
state: TN
zip: 763098
phoneNumbers:
- type: home
number: 0123456789
- type: work
number: 0987654321
In this example we again use the gopkg.in/yaml.v3
package to parse YAML data into a map. We will use the yaml.Unmarshal
function to parse the YAML data into a map[string]interface{}
which can hold any YAML data.
package main
import (
"fmt"
"io/ioutil"
"gopkg.in/yaml.v3"
)
func main() {
// Read the file
data, err := ioutil.ReadFile("config.yaml")
if err != nil {
fmt.Println(err)
return
}
// Create a map to hold the YAML data
var config map[string]interface{}
// Unmarshal the YAML data into the map
err = yaml.Unmarshal(data, &config)
if err != nil {
fmt.Println(err)
return
}
// Print the data
fmt.Println(config)
}
In this example, the config
variable is defined as map[string]interface{}
. This is because the yaml.Unmarshal
function returns a map with keys of type string and values of type interface{}
, which can hold any YAML value.
Then, the ioutil.ReadFile
function is used to read the contents of the file "config.yaml
" into the data
variable. Then the yaml.Unmarshal
function is used to parse the YAML data into the map config.
Output:
# go run main.go
map[address:map[city:Chennai state:TN street:123 Some Random Street zip:763098] age:35 name:Amit Kumar phoneNumbers:[map[number:1.23456789e+08 type:home] map[number:9.87654321e+08 type:work]]]
Example-3:Â Access individual nested fields of YAML File using map
Similar to our previous example where we accessed the data of YAML file using struct, similarly we can access individual fields or elements of the data by referencing them by their keys in the map.
fmt.Println(config["name"]) // Amit Kumar
To access the nested field, we need to use a type assertion to convert the value to the appropriate type. The type assertion should be used to assert that the nested field is of a certain type, in this case a map[string]interface{}
.
Here is an example of how we can access the "city
" field in the nested "address
" field:
address := config["address"].(map[string]interface{})
fmt.Println(address["city"]) // Chennai
To access the phone numbers in our example is more tricky as it involves some more operation. Here the phone numbers are stored in a slice so we need to iterate over the slice to get individual value. Inside the for loop, for each phone number, we use another type assertion to convert the phone number to a map of type map[string]interface{}
. Then it checks the value of type and if the value of the "type
" field is "home
", it prints the value of the "number
" field using fmt.Println
, but the value is of type interface{}
, it is printed as a byte slice.
Then we use strconv.FormatFloat
function, to convert the float64 to a string.
phoneNumbers := config["phoneNumbers"].([]interface{})
for _, phoneMap := range phoneNumbers {
phone := phoneMap.(map[string]interface{})
if phone["type"] == "home" {
fmt.Println(phone["number"]) // will print in byte format
// convert float64 to string
number := phone["number"].(float64)
numberString := strconv.FormatFloat(number, 'f', -1, 64)
fmt.Println(numberString) // 123456789
}
}
Summary
In this tutorial we learned how to parse YAML file (Simple and Complex with multiple nested keys) using golang yaml package. We use yaml.Unmarshal
to store the values of YAML key into struct or map and then access the individual elements.
References
https://pkg.go.dev/gopkg.in/yaml.v3
go - How to read a YAML file - Stack Overflow