or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

basic-operations.mdcustom-serialization.mdindex.mdnode-manipulation.mdstreaming-operations.md
tile.json

streaming-operations.mddocs/

Streaming YAML Operations

Stream-based encoding and decoding for processing multiple YAML documents or working with io.Reader and io.Writer interfaces. Use these when reading from or writing to files, network connections, or other I/O streams.

Decoder

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

type Decoder struct {
    // Has unexported fields
}

NewDecoder

Creates a new decoder that reads from an io.Reader.

func NewDecoder(r io.Reader) *Decoder

Parameters

  • r (io.Reader) - The input stream to read YAML documents from

Returns

  • *Decoder - A new decoder instance

Notes

  • The decoder introduces its own buffering
  • May read data from r beyond the YAML values requested

Decoder.Decode

Reads the next YAML-encoded value from the input stream and stores it in the value pointed to by v.

func (dec *Decoder) Decode(v interface{}) (err error)

Parameters

  • v (interface{}) - Pointer to the variable where the decoded value will be stored

Returns

  • err (error) - Returns io.EOF when no more documents are available. Returns *yaml.TypeError for type mismatch errors. Returns nil on success.

Behavior

  • Decodes one YAML document per call
  • Can be called multiple times to decode multiple documents from the same stream
  • Returns io.EOF when the stream is exhausted
  • See Basic YAML Operations for details on type conversion

Decoder.KnownFields

Enables strict field checking to ensure that keys in decoded mappings exist as fields in the struct being decoded into.

func (dec *Decoder) KnownFields(enable bool)

Parameters

  • enable (bool) - If true, unknown fields will cause an error

Behavior

  • When enabled, attempting to decode YAML with keys that don't match struct fields will result in an error
  • Useful for catching typos or unexpected fields in configuration files
  • Must be called before Decode()

Usage Example

Reading multiple YAML documents from a file:

package main

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

type Config struct {
    Name    string
    Version int
}

func main() {
    file, err := os.Open("configs.yaml")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    decoder := yaml.NewDecoder(file)

    // Read multiple documents
    for {
        var config Config
        err := decoder.Decode(&config)
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Config: %+v\n", config)
    }
}

Strict Field Checking Example

type Config struct {
    Name string
    Port int
}

data := `
name: MyApp
port: 8080
extra: value  # This field doesn't exist in Config struct
`

decoder := yaml.NewDecoder(strings.NewReader(data))
decoder.KnownFields(true)

var config Config
err := decoder.Decode(&config)
if err != nil {
    // Error: unknown field "extra"
    log.Println(err)
}

Encoder

An Encoder writes YAML values to an output stream.

type Encoder struct {
    // Has unexported fields
}

NewEncoder

Creates a new encoder that writes to an io.Writer.

func NewEncoder(w io.Writer) *Encoder

Parameters

  • w (io.Writer) - The output stream to write YAML documents to

Returns

  • *Encoder - A new encoder instance

Notes

  • The encoder should be closed after use to flush all data to w

Encoder.Encode

Writes the YAML encoding of v to the stream.

func (e *Encoder) Encode(v interface{}) (err error)

Parameters

  • v (interface{}) - The value to encode

Returns

  • err (error) - Error if encoding fails, nil otherwise

Behavior

  • If multiple items are encoded, the second and subsequent documents will be preceded with a --- document separator
  • The first document will not have a --- separator
  • See Basic YAML Operations for details on type conversion

Encoder.SetIndent

Changes the indentation used when encoding.

func (e *Encoder) SetIndent(spaces int)

Parameters

  • spaces (int) - Number of spaces to use for indentation. Must be non-negative.

Behavior

  • Default indentation is 2 spaces
  • Panics if spaces is negative
  • Must be called before Encode()

Encoder.Close

Closes the encoder by writing any remaining data.

func (e *Encoder) Close() (err error)

Returns

  • err (error) - Error if closing fails, nil otherwise

Notes

  • Does not write a stream terminating string "..."
  • Should always be called when done encoding to ensure all data is flushed

Usage Example

Writing multiple YAML documents to a file:

package main

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

type Config struct {
    Name    string
    Version int
}

func main() {
    file, err := os.Create("configs.yaml")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    encoder := yaml.NewEncoder(file)
    defer encoder.Close()

    configs := []Config{
        {Name: "App1", Version: 1},
        {Name: "App2", Version: 2},
        {Name: "App3", Version: 3},
    }

    for _, config := range configs {
        err := encoder.Encode(&config)
        if err != nil {
            log.Fatal(err)
        }
    }
}

// Output to file:
// name: App1
// version: 1
// ---
// name: App2
// version: 2
// ---
// name: App3
// version: 3

Custom Indentation Example

encoder := yaml.NewEncoder(os.Stdout)
encoder.SetIndent(4)  // Use 4 spaces instead of default 2
defer encoder.Close()

data := map[string]interface{}{
    "parent": map[string]interface{}{
        "child": map[string]string{
            "key": "value",
        },
    },
}

encoder.Encode(data)

// Output:
// parent:
//     child:
//         key: value

Common Patterns

Reading Configuration File

func LoadConfig(filename string) (*Config, error) {
    file, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer file.Close()

    var config Config
    decoder := yaml.NewDecoder(file)
    decoder.KnownFields(true)  // Strict checking

    if err := decoder.Decode(&config); err != nil {
        return nil, err
    }

    return &config, nil
}

Writing Configuration File

func SaveConfig(filename string, config *Config) error {
    file, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer file.Close()

    encoder := yaml.NewEncoder(file)
    defer encoder.Close()

    return encoder.Encode(config)
}

Processing Stream of Documents

func ProcessYAMLStream(r io.Reader) error {
    decoder := yaml.NewDecoder(r)

    for {
        var doc map[string]interface{}
        err := decoder.Decode(&doc)
        if err == io.EOF {
            break
        }
        if err != nil {
            return err
        }

        // Process document
        processDocument(doc)
    }

    return nil
}

Converting YAML Stream to JSON

func YAMLToJSON(r io.Reader, w io.Writer) error {
    decoder := yaml.NewDecoder(r)
    encoder := json.NewEncoder(w)

    for {
        var data interface{}
        err := decoder.Decode(&data)
        if err == io.EOF {
            break
        }
        if err != nil {
            return err
        }

        if err := encoder.Encode(data); err != nil {
            return err
        }
    }

    return nil
}

Error Handling

Both Decoder.Decode and Encoder.Encode can return errors:

  • io.EOF: Returned by Decode when no more documents are available
  • *yaml.TypeError: Type mismatch during decoding (see Basic YAML Operations)
  • Other errors: I/O errors, parsing errors, or encoding errors
decoder := yaml.NewDecoder(reader)
var config Config

err := decoder.Decode(&config)
switch {
case err == io.EOF:
    // No more documents
case err != nil:
    if typeErr, ok := err.(*yaml.TypeError); ok {
        // Handle type errors
        for _, e := range typeErr.Errors {
            log.Println(e)
        }
    } else {
        // Handle other errors
        log.Fatal(err)
    }
}

Notes

  • For simple single-document marshaling/unmarshaling, use yaml.Marshal and yaml.Unmarshal instead (see Basic YAML Operations)
  • For low-level AST manipulation, use Node Manipulation
  • Always close encoders with Close() to ensure data is flushed
  • Decoders automatically handle buffering and can read multiple documents