goccy/go-yaml Documentation

repository·master·Indexed 24 days ago

https://github.com/goccy/go-yaml

A high-performance, feature-rich YAML support library for Go designed to replace go-yaml/yaml. It provides reflection-based encoding and decoding via yaml.Marshal and yaml.Unmarshal, support for custom marshaler/unmarshaler interfaces, and advanced features like explicit/implicit anchors and aliases, YAMLPath queries, and reference resolution across external files. The library includes a Decoder for stream parsing, an Encoder for writing YAML, and the ycat CLI tool for syntax-highlighted YAML viewing.

Tokens
8.5K
Snippets
18
Records
60
Agent score
81%

What's inside goccy/go-yaml

  1. Implement Custom Marshaler and Unmarshaler

    master

    You can customize how types are handled by implementing either the Bytes or Interface variants of the marshaler/unmarshaler interfaces:

    • BytesMarshaler / BytesUnmarshaler: Behaves like encoding/json. It returns/accepts []byte. Note that because indentation matters in YAML, the library must decode the returned bytes to integrate them correctly into the parent container.
    • InterfaceMarshaler / InterfaceUnmarshaler: Behaves like gopkg.in/yaml.v2. This is generally more performant for complex objects because it allows the library to skip an extra decoding step.
  2. Simple Encode and Decode YAML

    master

    The library provides an interface similar to go-yaml/yaml using reflection. You can use yaml.Marshal to convert Go structs to YAML bytes and yaml.Unmarshal to parse YAML bytes into Go structs.

    To control field mapping, use the yaml struct tag. The library also supports the json tag as a fallback, though yaml tags take precedence if both are present.

    var v struct {
    	A int
    	B string
    }
    v.A = 1
    v.B = "hello"
    bytes, err := yaml.Marshal(v)
    if err != nil {
    	//...
    }
    fmt.Println(string(bytes)) // "a: 1\nb: hello\n"
  3. What is a YAMLPath and how does it work?

    master

    A Path represents a YAMLPath (similar to JSONPath) used to navigate the Abstract Syntax Tree (AST) of a YAML document. It acts as a selector that can be used to:

    1. Filter: Find specific nodes within an ast.File or ast.Node.
    2. Read: Extract and unmarshal data from an io.Reader.
    3. Merge: Combine data from one source into another at a specific path.
    4. Replace: Swap out existing nodes with new ones at a specific path.

    Internally, the Path is composed of a chain of pathNode implementations (like selectorNode, indexNode, or recursiveNode) that execute sequentially to traverse the YAML tree.

  4. Implement custom YAML marshaling

    master

    You can control how specific types are encoded by implementing one of the following interfaces:

    • BytesMarshalerContext: MarshalYAML(ctx context.Context) ([]byte, error)
    • BytesMarshaler: MarshalYAML() ([]byte, error)
    • InterfaceMarshalerContext: MarshalYAML(ctx context.Context) (interface{}, error)
    • InterfaceMarshaler: MarshalYAML() (interface{}, error)
    • encoding.TextMarshaler: MarshalText() ([]byte, error)

    Additionally, if the useJSONMarshaler option is enabled, types implementing json.Marshaler will be encoded via their JSON representation.

  5. Implement custom marshaling and unmarshaling interfaces

    master

    To customize how a type is handled during YAML operations, implement one of the following interfaces:

    Marshaling Interfaces:

    • BytesMarshaler: MarshalYAML() ([]byte, error)
    • BytesMarshalerContext: MarshalYAML(context.Context) ([]byte, error)
    • InterfaceMarshaler: MarshalYAML() (interface{}, error) (compatible with github.com/go-yaml/yaml)
    • InterfaceMarshalerContext: MarshalYAML(context.Context) (interface{}, error)

    Unmarshaling Interfaces:

    • BytesUnmarshaler: UnmarshalYAML([]byte) error
    • BytesUnmarshalerContext: UnmarshalYAML(context.Context, []byte) error
    • InterfaceUnmarshaler: UnmarshalYAML(func(interface{}) error) error (compatible with github.com/go-yaml/yaml)
    • InterfaceUnmarshalerContext: UnmarshalYAML(context.Context, func(interface{}) error) error
    • NodeUnmarshaler: UnmarshalYAML(ast.Node) error (provides the AST node instead of raw bytes)
    • NodeUnmarshalerContext: UnmarshalYAML(context.Context, ast.Node) error
  6. Handle decoding errors and unknown fields

    master
    When decoding into structs, the Decoder behavior regarding unknown fields depends on the disallowUnknownField configuration. If enabled, the decoder will return an error if it encounters a key in the YAML that does not match a field in the destination struct. The decoder also supports validation via a validator interface; if a validation error occurs, the decoder attempts to associate the error with the corresponding YAML token/location to provide better error context.
  7. Control field omission with omitempty and omitzero

    master

    The encoder supports omitting fields based on tags and options:

    • omitempty tag: Omits a field if it is considered 'empty' (e.g., zero value for primitives, nil for pointers/slices/maps, or if the type implements IsZero()).
    • omitzero tag: Omits a field if it is the zero value of its type.
    • Encoder.omitEmpty option: Omits fields if they are empty.
    • Encoder.omitZero option: Omits fields if they are the zero value.

    Note: The current implementation of omitempty combines the behavior of encoding/json's omitempty and omitzero to maintain compatibility with older YAML libraries.

  8. Encode with implicit Anchor and Alias names

    master

    If you use the anchor tag without specifying a name, the library defaults to using the lowercase version of the field name (e.g., strings.ToLower($FieldName)). Identical pointer addresses will automatically trigger alias generation.

    type T struct {
    	I int
    	S string
    }
    var v struct {
    	A *T `yaml:"a,anchor"`
    	B *T `yaml:"b,anchor"`
    	C *T `yaml:"c"`
    	D *T `yaml:"d"`
    }
    v.A = &T{I: 1, S: "hello"}
    v.B = &T{I: 2, S: "world"}
    v.C = v.A // C has same pointer address to A
    v.D = v.B // D has same pointer address to B
    bytes, err := yaml.Marshal(v)
    if err != nil {
    	//...
    }
    fmt.Println(string(bytes))
    /*
    a: &a
      i: 1
      s: hello
    b: &b
      i: 2
      s: world
    c: *a
    d: *b
    */
  9. Use MergeKey and Alias with embedded structs

    master

    To use the YAML merge key (<<: *alias) pattern, you can embed a struct type using the inline,alias tags. This is useful for providing default values that can be overridden by the parent struct.

    type Person struct {
    	*Person `yaml:",omitempty,inline,alias"` // embed Person type for default value
    	Name    string `yaml:",omitempty"`
    	Age     int    `yaml:",omitempty"`
    }
    defaultPerson := &Person{
    	Name: "John Smith",
    	Age:  20,
    }
    people := []*Person{
    	{
    		Person: defaultPerson, // assign default value
    		Name:   "Ken",         // override Name property
    		Age:    10,            // override Age property
    	},
    	{
    		Person: defaultPerson, // assign default value only
    	},
    }
    var doc struct {
    	Default *Person   `yaml:"default,anchor"`
    	People  []*Person `yaml:"people"`
    }
    doc.Default = defaultPerson
    doc.People = people
    bytes, err := yaml.Marshal(doc)
    if err != nil {
    	//...
    }
    fmt.Println(string(bytes))
    /*
    default: &default
      name: John Smith
      age: 20
    people:
    - <<: *default
      name: Ken
      age: 10
    - <<: *default
    */
  10. Encode with explicit Anchor and Alias names

    master

    You can explicitly name anchors and aliases using struct tags. If a pointer type is assigned to multiple fields and the addresses are identical, the library automatically treats them as aliases. If you specify an explicit alias name via a tag, an error is raised if the value does not match the specified anchor.

    type T struct {
      A int
      B string
    }
    var v struct {
      C *T `yaml:"c,anchor=x"`
      D *T `yaml:"d,alias=x"`
    }
    v.C = &T{A: 1, B: "hello"}
    v.D = v.C
    bytes, err := yaml.Marshal(v)
    if err != nil {
      panic(err)
    }
    fmt.Println(string(bytes))
    /*
    c: &x
      a: 1
      b: hello
    d: *x
    */
  11. Annotate errors with YAML source code

    master

    To provide better debugging information, you can use yaml.PathString to locate a specific node and then call AnnotateSource to retrieve the segment of the original YAML source code corresponding to that path. This is useful for printing customized error messages that show exactly where a validation failed.

    package main
    
    import (
      "fmt"
    
      "github.com/goccy/go-yaml"
    )
    
    func main() {
      yml := `
    a: 1
    b: "hello"
    `
      var v struct {
        A int
        B string
      }
      if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
        panic(err)
      }
      if v.A != 2 {
        // output error with YAML source
        path, err := yaml.PathString("$.a")
        if err != nil {
          panic(err)
        }
        source, err := path.AnnotateSource([]byte(yml), true)
        if err != nil {
          panic(err)
        }
        fmt.Printf("a value expected 2 but actual %d:\n%s\n", v.A, string(source))
      }
    }