gyaml

package module
v0.40.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 14, 2026 License: BSD-2-Clause Imports: 16 Imported by: 0

README

A fast and efficient Go YAML encoder/decoder

Introduction

The gyaml package enables Go programs to encode and decode YAML text. Inspired by go-yaml and standard encoding/json projects, featuring great performance and flexibility.

Performance comparison (see Makefile & example/main.go for a synthetic example):

  • Encoding faster upto 12-15 times than go-yaml, 10-12 times less memory & gc pressure (make encode and make encodev2 for a little bit more complex data structure)
  • Decoding faster upto 7-9 times than go-yaml, 15-18 times less memory & gc pressure (make decode and make decodev2, run them only after encoders have created a corresponding test yaml file)

Usage example

package main

import (
	"log"
	"os"
	"time"

	"github.com/dbakhtin/gyaml"
)

type DatabaseConfig struct {
	Host     string
	User     string
	PoolSize int `yaml:"pool_size"`
}

type Config struct {
	DB       DatabaseConfig `yaml:"database"`
	Priority float64
	Date     time.Time `yaml:"some_date"`
}

func encode() {
	conf := Config{
		Priority: 3.4,
		Date:     time.Now(),
		DB: DatabaseConfig{
			Host:     "localhost:6234",
			User:     "sup",
			PoolSize: 12,
		},
	}
	file, err := os.OpenFile("config.yaml", os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		log.Fatalf("error creating sample config.yaml: %v", err)
	}
	defer file.Close()
	if err := gyaml.NewEncoder(file).Encode(conf); err != nil {
		log.Fatalf("error encoding config: %v", err)
	}
	log.Println("Encoding successful")
}

func decode() {
	file, err := os.OpenFile("config.yaml", os.O_RDONLY, 0644)
	if err != nil {
		log.Fatalf("error opening config.yaml: %v", err)
	}
	defer file.Close()
	var conf Config

	if err := gyaml.NewDecoder(file).Decode(&conf); err != nil {
		log.Fatalf("error decoding config.yaml: %v", err)
	}
	log.Printf("Decoding successful: %+v\n", conf)
}

func main() {
	encode()
	decode()
}

Compatibility

The gyaml package supports most of YAML 1.2 and does not support merging operator << from YAML 1.1.

Attention

Although all tests are passing, errors may occur. Please file an issue with a brief example to help me debug and improve the code. Thank you.

TODO

  • Decoding multi-dimensional block sequences "- - a", "- - - a" (encoding works already). For now use mixed "- [a, b]" or full flow [[a,b],..] styles.
  • [a: b, c: d] syntax (an array with nested objects (key: value without {})).
  • Improve performance.

License

BSD-2 simplified license, see LICENSE file.

Documentation

Overview

Package gyaml implements encoding and decoding of YAML. The mapping between YAML and Go values is described in the documentation for the Marshal and Unmarshal functions.

Index

Constants

View Source
const DefaultIndentSize = 2

Variables

This section is empty.

Functions

func Marshal

func Marshal(v any) ([]byte, error)

Marshal returns the YAML encoding of v.

Marshal traverses the value v recursively. If an encountered value implements Marshaler and is not a nil pointer, Marshal calls [Marshaler.MarshalYAML] to produce YAML. If no [Marshaler.MarshalYAML] method is present but the value implements encoding.TextMarshaler instead, Marshal calls encoding.TextMarshaler.MarshalText and encodes the result as a YAML string. The nil pointer exception is not strictly necessary but mimics a similar, necessary exception in the behavior of [Unmarshaler.UnmarshalYAML].

Otherwise, Marshal uses the following type-dependent default encodings:

Boolean values encode as YAML booleans.

Floating point, integer, and [Number] values encode as YAML numbers.

String values encode as YAML unquoted strings, unless it passes the IsNeedQuoted function check for ambiguities: if it consists of a pure number, time.Time, boolean value, etc.

Array and slice values encode as YAML sequences, by default using a block style. For flow style you need to set a "flow" tag or use a global flow style for encoder.

Struct values encode as YAML objects. Again optional flow style is supported. Each exported struct field becomes a member of the object, using the lowercased field name as the object key, unless the field is omitted for one of the reasons given below.

The encoding of each struct field can be customized by the format string stored under the "yaml" key in the struct field's tag. The format string gives the name of the field, possibly followed by a comma-separated list of options. The name may be empty in order to specify options without overriding the default field name.

The "omitempty" option specifies that the field should be omitted from the encoding if the field has an empty value, defined as false, 0, a nil pointer, a nil interface value, and any array, slice, map, or string of length zero.

As a special case, if the field tag is "-", the field is always omitted. Note that a field with name "-" can still be generated using the tag "-,".

Examples of struct field tags and their meanings:

// Field appears in YAML as key "myName".
Field int `yaml:"myName"`

// Field appears in YAML as key "myName" and
// the field is omitted from the object if its value is empty,
// as defined above.
Field int `yaml:"myName,omitempty"`

// Field appears in YAML as key "Field" (the default), but
// the field is skipped if empty.
// Note the leading comma.
Field int `yaml:",omitempty"`

// Field is ignored by this package.
Field int `yaml:"-"`

// Field appears in YAML as key "-".
Field int `yaml:"-,"`

The "omitzero" option specifies that the field should be omitted from the encoding if the field has a zero value, according to rules:

1) If the field type has an "IsZero() bool" method, that will be used to determine whether the value is zero.

2) Otherwise, the value is zero if it is the zero value for its type.

If both "omitempty" and "omitzero" are specified, the field will be omitted if the value is either empty or zero (or both).

Embedded struct fields are usually marshaled as if their inner exported fields were fields in the outer struct, subject to the usual Go visibility rules amended as described in the next paragraph. An anonymous struct field with a name given in its YAML tag is treated as having that name, rather than being anonymous. An anonymous struct field of interface type is treated the same as having that type as its name, rather than being anonymous.

The Go visibility rules for struct fields are amended for YAML when deciding which field to marshal or unmarshal. If there are multiple fields at the same level, and that level is the least nested (and would therefore be the nesting level selected by the usual Go rules), the following extra rules apply:

1) Of those fields, if any are YAML-tagged, only tagged fields are considered, even if there are multiple untagged fields that would otherwise conflict.

2) If there is exactly one field (tagged or not according to the first rule), that is selected.

3) Otherwise there are multiple fields, and all are ignored; no error occurs.

Handling of anonymous struct fields is new in Go 1.1. Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of an anonymous struct field in both current and earlier versions, give the field a YAML tag of "-".

Map values encode as YAML objects. The map's key type must either be a string, an integer type, uintptr, float, bool, or implement encoding.TextMarshaler. The map keys are sorted and used as YAML object keys by applying the following rules, subject to the UTF-8 coercion described for string values above:

  • keys of any string type are used directly
  • keys that implement encoding.TextMarshaler are marshaled
  • number and bool keys are converted to strings

Pointer values encode as the value pointed to unless anchor is found. A nil pointer encodes as the null YAML value.

Interface values encode as the value contained in the interface. A nil interface value encodes as the null YAML value.

Channel, complex, and function values cannot be encoded in YAML. Attempting to encode such a value causes Marshal to return an UnsupportedTypeError.

YAML cannot represent cyclic data structures and Marshal does not handle them. Passing cyclic structures to Marshal will result in an error.

func MarshalWithOptions

func MarshalWithOptions(v any, opts EncoderOptions) ([]byte, error)

MarshalWithOptions calls Marshal with specified global options, see EncoderOptions

func Unmarshal

func Unmarshal(data []byte, v any) error

Unmarshal decodes the first document found within the in byte slice and assigns decoded values into the out value.

Struct fields are only unmarshalled if they are exported, and are unmarshalled using the field name lowercased as the default key. Custom name may be defined via the "yaml" field tag: the content preceding the first comma is used as the key, and the following comma-separated options are used to tweak the marshaling process (see Marshal). Conflicting names result in a runtime error.

For example:

type T struct {
    F int `yaml:"a,omitempty"`
    B int
}
var t T
yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)

See the documentation of Marshal for the format of tags and a list of supported tag options.

func Valid

func Valid(data []byte) bool

Valid reports whether data is a valid YAML encoding.

Types

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

A Decoder reads and decodes YAML values from an input stream.

func NewDecoder

func NewDecoder(r io.Reader) *Decoder

NewDecoder returns a new decoder that reads from r.

The decoder introduces its own buffering and may read data from r beyond the YAML values requested.

func (*Decoder) Buffered

func (dec *Decoder) Buffered() io.Reader

Buffered returns a reader of the data remaining in the Decoder's buffer. The reader is valid until the next call to Decoder.Decode.

func (*Decoder) Decode

func (dec *Decoder) Decode(v any) error

Decode reads the next YAML-encoded value from its input and stores it in the value pointed to by v.

See the documentation for Unmarshal for details about the conversion of YAML into a Go value.

func (*Decoder) DisallowUnknownFields

func (dec *Decoder) DisallowUnknownFields()

DisallowUnknownFields causes the Decoder to return an error when the destination is a struct and the input contains object keys which do not match any non-ignored, exported fields in the destination.

func (*Decoder) InputOffset

func (dec *Decoder) InputOffset() int64

InputOffset returns the input stream byte offset of the current decoder position. The offset gives the location of the end of the most recently returned token and the beginning of the next token.

func (*Decoder) More

func (dec *Decoder) More() bool

More reports whether there is another element in the current array or object being parsed.

func (*Decoder) Token

func (dec *Decoder) Token() (Token, error)

Token returns the next YAML token in the input stream. At the end of the input stream, Token returns nil, io.EOF.

Token guarantees that the delimiters [ ] { } it returns are properly nested and matched: if Token encounters an unexpected delimiter in the input, it will return an error.

The input stream consists of basic YAML values—bool, string, number, and null—along with delimiters [ ] { } of type Delim to mark the start and end of arrays and objects. Commas and colons are elided.

type Delim

type Delim rune

A Delim is a YAML array or object delimiter, one of [ ] { or }.

func (Delim) String

func (d Delim) String() string

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

An Encoder writes YAML values to an output stream.

func NewEncoder

func NewEncoder(w io.Writer) *Encoder

NewEncoder returns a new encoder that writes to w.

func (*Encoder) Encode

func (e *Encoder) Encode(v any) error

Encode writes the Yaml encoding of v to the stream, followed by a newline character.

If multiple items are encoded to the stream, the second and subsequent document will be preceded with a "---" document separator, but the first will not.

See the documentation for Marshal for details about the conversion of Go values to Yaml.

func (*Encoder) WithAutoInt

func (e *Encoder) WithAutoInt(yes bool) *Encoder

WithAutoInt automatically converts floating-point numbers to integers when the fractional part is zero. For example, a value of 1.0 will be encoded as 1.

func (*Encoder) WithFlowStyle

func (e *Encoder) WithFlowStyle(yes bool) *Encoder

WithFlowStyle style for sequences

func (*Encoder) WithIndent

func (e *Encoder) WithIndent(size int) *Encoder

WithIndent changes default indent

func (*Encoder) WithIndentSequence

func (e *Encoder) WithIndentSequence(yes bool) *Encoder

WithIndentSequence causes sequence values to be indented the same value as Indent

func (*Encoder) WithJSONStyle

func (e *Encoder) WithJSONStyle(yes bool) *Encoder

WithJSONStyle uses json style for encoding

func (*Encoder) WithLiteralMultilineStyle

func (e *Encoder) WithLiteralMultilineStyle(yes bool) *Encoder

WithLiteralMultilineStyle causes encoding multiline strings with a literal syntax, no matter what characters they include

func (*Encoder) WithOmitEmpty

func (e *Encoder) WithOmitEmpty(yes bool) *Encoder

WithOmitEmpty behaves in the same way as the interpretation of the omitempty tag in the encoding/json library. set on all the fields. In the current implementation, the omitempty tag is not implemented in the same way as encoding/json, so please specify this option if you expect the same behavior.

func (*Encoder) WithOmitZero

func (e *Encoder) WithOmitZero(yes bool) *Encoder

WithOmitZero forces the encoder to assume an `omitzero` struct tag is set on all the fields. See `Marshal` commentary for the `omitzero` tag logic.

func (*Encoder) WithOptions

func (e *Encoder) WithOptions(opts EncoderOptions) *Encoder

func (*Encoder) WithSingleQuote

func (e *Encoder) WithSingleQuote(yes bool) *Encoder

WithSingleQuote determines if single or double quotes should be preferred for strings.

type EncoderOptions

type EncoderOptions struct {
	SingleQuote           bool
	FlowStyle             bool
	JSONStyle             bool
	OmitZero              bool
	OmitEmpty             bool
	AutoInt               bool
	LiteralStyleMultiline bool
	IndentSequence        bool

	//default indent size in spaces
	IndentSize int
}

EncoderOptions are global encoder options

func DefaultEncoderOptions

func DefaultEncoderOptions() EncoderOptions

type InvalidUnmarshalError

type InvalidUnmarshalError struct {
	Type reflect.Type
}

An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. (The argument to Unmarshal must be a non-nil pointer.)

func (*InvalidUnmarshalError) Error

func (e *InvalidUnmarshalError) Error() string

type Marshaler

type Marshaler interface {
	MarshalYAML() ([]byte, error)
}

Marshaler is the interface implemented by types that can marshal themselves into valid YAML.

type MarshalerError

type MarshalerError struct {
	Type reflect.Type
	Err  error
	// contains filtered or unexported fields
}

A MarshalerError represents an error from calling a [Marshaler.MarshalYAML] or encoding.TextMarshaler.MarshalText method.

func (*MarshalerError) Error

func (e *MarshalerError) Error() string

func (*MarshalerError) Unwrap

func (e *MarshalerError) Unwrap() error

Unwrap returns the underlying error.

type RawMessage

type RawMessage []byte

RawMessage is a raw encoded YAML value. It implements Marshaler and Unmarshaler and can be used to delay YAML decoding or precompute a YAML encoding.

func (RawMessage) MarshalYAML

func (m RawMessage) MarshalYAML() ([]byte, error)

MarshalYAML returns m as the YAML encoding of m.

func (*RawMessage) UnmarshalYAML

func (m *RawMessage) UnmarshalYAML(data []byte) error

UnmarshalYAML sets *m to a copy of data.

type SyntaxError

type SyntaxError struct {
	Offset int64 // error occurred after reading Offset bytes
	// contains filtered or unexported fields
}

A SyntaxError is a description of a YAML syntax error. Unmarshal will return a SyntaxError if the YAML can't be parsed.

func (*SyntaxError) Error

func (e *SyntaxError) Error() string

type Token

type Token any

A Token holds a value of one of these types:

  • Delim, for the four YAML delimiters [ ] { }
  • bool, for YAML booleans
  • float64, for YAML numbers
  • [Number], for YAML numbers
  • string, for YAML string literals
  • nil, for YAML null

type UnmarshalError

type UnmarshalError struct {
	Offset  int
	Type    reflect.Type
	Message string
}

An UnmarshalError describes a generic YAML unmarshal error Is intended to replace original (coming from json) panics with errors for readability

func (*UnmarshalError) Error

func (e *UnmarshalError) Error() string

type UnmarshalTypeError

type UnmarshalTypeError struct {
	Value  string       // description of YAML value - "bool", "array", "number -5"
	Type   reflect.Type // type of Go value it could not be assigned to
	Offset int64        // error occurred after reading Offset bytes
	Struct string       // name of the struct type containing the field
	Field  string       // the full path from root node to the field, include embedded struct
}

An UnmarshalTypeError describes a YAML value that was not appropriate for a value of a specific Go type.

func (*UnmarshalTypeError) Error

func (e *UnmarshalTypeError) Error() string

type Unmarshaler

type Unmarshaler interface {
	UnmarshalYAML([]byte) error
}

Unmarshaler is the interface implemented by types that can unmarshal a YAML description of themselves. The input can be assumed to be a valid encoding of a YAML value. UnmarshalYAML must copy the YAML data if it wishes to retain the data after returning.

type UnsupportedTypeError

type UnsupportedTypeError struct {
	Type reflect.Type
}

An UnsupportedTypeError is returned by Marshal when attempting to encode an unsupported value type.

func (*UnsupportedTypeError) Error

func (e *UnsupportedTypeError) Error() string

type UnsupportedValueError

type UnsupportedValueError struct {
	Value reflect.Value
	Str   string
}

An UnsupportedValueError is returned by Marshal when attempting to encode an unsupported value.

func (*UnsupportedValueError) Error

func (e *UnsupportedValueError) Error() string

Directories

Path Synopsis