YAML in Go: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
Line 14: Line 14:
{{Note|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.}}  
{{Note|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.}}  


<span id='TODO_Ee4'></span><font color=darkkhaki>Come up with an in-depth explanation for this.</font>
=Example=
 
<syntaxhighlight lang='go'>
<syntaxhighlight lang='go'>
package main
package main
Line 49: Line 48:
Options []string `yaml:"options"`
Options []string `yaml:"options"`
}
}


func main() {
func main() {
config := Config{
config := Config{
Color: "blue",
Color: "blue",
Line 62: Line 59:
},
},
}
}
f, err := os.Create("/Users/ovidiu/tmp/test.yaml")
f, err := os.Create("/Users/ovidiu/tmp/test.yaml")
if err != nil { ... }
if err != nil { ... }
//
//
// Marshal recursive memory struct into a file
// Marshal recursive memory struct into a file
//
//
if err := yaml.NewEncoder(f).Encode(&config); err != nil { ... }
if err := yaml.NewEncoder(f).Encode(&config); err != nil { ... }
if err = f.Close(); err != nil { ... }
if err = f.Close(); err != nil { ... }
fmt.Printf("yaml file written and closed\n")
fmt.Printf("yaml file written and closed\n")
f, err = os.Open("/Users/ovidiu/tmp/test.yaml")
f, err = os.Open("/Users/ovidiu/tmp/test.yaml")
if err != nil { ... }
if err != nil { ... }
Line 80: Line 72:
if err = f.Close(); err != nil { ... }
if err = f.Close(); err != nil { ... }
}()
}()
//
//
// Unmarshall the file into a different memory struct
// Unmarshall the file into a different memory struct
Line 98: Line 89:
}
}
</syntaxhighlight>
</syntaxhighlight>
=Behavior when an Entire Subtree Is Missing=
=Behavior when an Entire Subtree Is Missing=


If a YAML node (subtree) is missing, the corresponding <code>struct</code> field contains the [[Go_Structs#struct_Zero_Value|<tt>struct</tt> zero value]] for the corresponding <tt>struct</tt> type, so it is safe to access it. Just be mindful you will get the zero values, recursively.
If a YAML node (subtree) is missing, the corresponding <code>struct</code> field contains the [[Go_Structs#struct_Zero_Value|<tt>struct</tt> zero value]] for the corresponding <tt>struct</tt> type, so it is safe to access it. Just be mindful you will get the zero values, recursively.

Revision as of 22:03, 14 March 2024

External

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.

Example

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

If a YAML node (subtree) is missing, the corresponding struct field contains the struct zero value for the corresponding struct type, so it is safe to access it. Just be mindful you will get the zero values, recursively.