yaml.zig

YAML for Zig: read it into your structs, write your values back, and check files from the command line. For Python, a drop-in for PyYAML.

Install The library GitHub

  • Complete. All of YAML 1.2.2. It passes the whole yaml-test-suite: every valid file read correctly, every invalid file refused.
  • Fast. A little faster than rapidyaml (C++) at building a document tree, and three to four times faster than serde_yaml, saphyr and js-yaml at reading into values. The numbers.
  • Safe on files you did not write. Alias bombs, deep nesting, hash flooding and merge fan-out are all capped, and no tag can run code.
  • Clear errors. Line, column, the path to the value, and a hint: 4:3: server: unknown field "tag"; expected one of: hosts, tags, tls, root (did you mean "tags"?).
  • A command-line tool. yaml check, yaml lint, yaml get, yaml json.
  • Python too. pyyaml-zig is a drop-in for PyYAML on this parser, 37 times faster at loading.

A quick look

const std = @import("std");
const yaml = @import("yaml");

test "quick start: read a document into a struct" {
    const Config = struct {
        name: []const u8,
        port: u16 = 8080,
        tags: []const []const u8 = &.{},
        debug: bool = false,
    };
    const text =
        \\name: web
        \\tags: [blue, green]
        \\debug: true
        \\
    ;

    // The result owns an arena holding everything it points at.
    const parsed = try yaml.parseFromSlice(Config, std.testing.allocator, text, .{});
    defer parsed.deinit();

    try std.testing.expectEqualStrings("web", parsed.value.name);
    try std.testing.expectEqual(8080, parsed.value.port); // not in the text: the default
    try std.testing.expectEqualStrings("green", parsed.value.tags[1]);
    try std.testing.expect(parsed.value.debug);
}
$ yaml check config.yaml deploy.yaml
deploy.yaml:2:1: a mapping cannot have two equal keys
a: 2
^
import pyyaml_zig as yaml  # was: import yaml

with open("config.yaml") as f:
    config = yaml.safe_load(f)

Install

For How
Zig 0.17.1 zig fetch --save=yaml git+https://github.com/kmoneil/yaml.zig, then add the import in build.zig (below)
The yaml command brew install kmoneil/tap/yaml on macOS and Linux, or a binary from the latest release for Linux, macOS and Windows on x86_64 and arm64
Python 3.9 and later pip install pyyaml-zig: wheels for Linux, macOS and Windows, x86_64 and arm64, with nothing to compile
const yaml = b.dependency("yaml", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("yaml", yaml.module("yaml"));

Put #v0.2.0 (or another release tag) after the fetch URL to pin a release. Every release file comes with SHA256SUMS and a build-provenance attestation.

Where next

  • The library: read into structs or any shape, write, errors, merge keys, and files written for YAML 1.1.
  • The guide: types that parse themselves, the stages under parseFromSlice, and exactly what yaml.zig reads.
  • The yaml command: check, lint, get, json and the rest.
  • Python: pyyaml-zig, and switching from PyYAML.
  • Speed and safety: the comparison, and the limits that keep hostile files cheap.