Parsing and Serializing YAML in Go Without Struct Definitions
This article is a translated version of my original post on Qiita. Original (Japanese): https://qiita.com/segur/items/677746b7e3d55d0d66b9
Introduction
I wanted to parse YAML and convert it back to a string in Go, but I couldn't find an article that showed how to do it without defining a struct. So I'm writing it down before I forget.
The sample source code is available here: https://github.com/segurvita/yaml-encoding-practice
The YAML Used in This Example
I'm reusing a swagger.yaml from a previous article.
swagger: '2.0'
info:
description: This is an API for apartments.
version: 0.0.1
title: Apartment API
paths:
'/rooms/{room-id}':
get:
summary: Room Info API
description: Returns information for the specified room-id
parameters:
- name: room-id
in: path
description: The ID of the room to retrieve
required: true
type: integer
format: int64
responses:
'200':
description: OK
schema:
type: object
properties:
id:
type: integer
format: int64
example: 404
comment:
type: string
example: Room 404. Maybe it doesn't exist anywhere.
Sample Code
Here is the content of main.go. It converts a YAML string into an object, then converts that object back into a YAML string — not particularly useful on its own, but good for demonstrating the approach.
package main
import "fmt"
import "gopkg.in/yaml.v2"
func main() {
// Define the input YAML
yamlInput := []byte(`
swagger: '2.0'
info:
description: This is an API for apartments.
version: 0.0.1
title: Apartment API
paths:
'/rooms/{room-id}':
get:
summary: Room Info API
description: Returns information for the specified room-id
parameters:
- name: room-id
in: path
description: The ID of the room to retrieve
required: true
type: integer
format: int64
responses:
'200':
description: OK
schema:
type: object
properties:
id:
type: integer
format: int64
example: 404
comment:
type: string
example: Room 404. Maybe it doesn't exist anywhere.
`)
// Convert YAML to object
var objInput interface{}
err := yaml.Unmarshal(yamlInput, &objInput)
if err != nil {
fmt.Println("Error: ", err)
}
// Convert object to YAML
yamlOutput, err := yaml.Marshal(&objInput)
if err != nil {
fmt.Println("Error: ", err)
}
// Print to stdout
fmt.Println("# ------------------------------------------------------------")
fmt.Println("# Input YAML:")
fmt.Println("# ------------------------------------------------------------")
fmt.Println(string(yamlInput), "\n")
fmt.Println("# ------------------------------------------------------------")
fmt.Println("# Object:")
fmt.Println("# ------------------------------------------------------------")
fmt.Println(objInput, "\n")
fmt.Println("# ------------------------------------------------------------")
fmt.Println("# Output YAML:")
fmt.Println("# ------------------------------------------------------------")
fmt.Println(string(yamlOutput), "\n")
}
Explanation
I used go-yaml for YAML processing.
Unmarshalis the parse operation — it converts a string into an object.Marshalis the stringify operation — it converts an object into a string.
By passing an interface{} variable as the second argument to Unmarshal, we avoid the need to define a struct. Very convenient!
Marshal also works without a struct definition.
Output
Running the code above produces the following output:
info:
description: This is an API for apartments.
title: Apartment API
version: 0.0.1
paths:
/rooms/{room-id}:
get:
description: Returns information for the specified room-id
parameters:
- description: The ID of the room to retrieve
format: int64
in: path
name: room-id
required: true
type: integer
responses:
"200":
description: OK
schema:
properties:
comment:
example: Room 404. Maybe it doesn't exist anywhere.
type: string
id:
example: 404
format: int64
type: integer
type: object
summary: Room Info API
swagger: "2.0"
The keys at each level were sorted alphabetically, but the content is exactly the same as the input YAML.
References
Closing
Easier than I expected — glad it worked out!