YAML in Go

From NovaOrdis Knowledge Base
Revision as of 17:57, 5 January 2024 by Ovidiu (talk | contribs) (→‎Overview)
Jump to navigation Jump to search

Internal

Overview

Declare a recursive structure that matches the structure of the YAML file, and then use a YAML encoder/decoder to marshall/unmarshall data in and out.

YAML support is available in the gopkg.in/yaml.v3 package.


If the structure fields are not capitalized, they are not visible across packages, and their content will zero-ed. The structure field will be accessible, but its content will be the corresponding zero value for the type.

Come up with an in-depth explanation for this.

package main

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

//
// color: "blue:
// details:
//   size: 10
//   weight 2.2
//   used: true
//   options:
//     - light
//     - medium
//     - heavy
//

// Note: if the package name is "config", name the structure differently, config.Config does not work well
type Config struct {
	Color   string  `yaml:"color"`
	Details Details `yaml:"details"`
}

type Details struct {
	Size    int      `yaml:"size"`
	Weight  float64  `yaml:"weight"`
	Used    bool     `yaml:"used"`
	Options []string `yaml:"options"`
}


func main() {

	config := Config{
		Color: "blue",
		Details: Details{
			Size:    10,
			Weight:  2.2,
			Used:    true,
			Options: []string{"light", "medium", "heavy"},
		},
	}

	f, err := os.Create("/Users/ovidiu/tmp/test.yaml")
	if err != nil { ... }

	//
	// Marshal recursive memory struct into a file
	//

	if err := yaml.NewEncoder(f).Encode(&config); err != nil { ... }

	if err = f.Close(); err != nil { ... }
	fmt.Printf("yaml file written and closed\n")

	f, err = os.Open("/Users/ovidiu/tmp/test.yaml")
	if err != nil { ... }
	defer func() {
		if err = f.Close(); err != nil { ... }
	}()

	//
	// Unmarshall the file into a different memory struct
	//
	config2 := Config{}
	err = yaml.NewDecoder(f).Decode(&config2)
    // the decoder return io.EOF on an empty YAML file, even if the file contains
    // comments. We don't want to handle this as error, but as simply "empty file"
    if errors.Is(err, io.EOF) {
      // nothing to do, the config is empty anyway
      log.Printf("empty configuration\n")
    } else {
       // handle as error
       ...
    }
	fmt.Printf("%+v\n", config2)
}

Behavior when an Entire Subtree Is Missing