Skip to content

Reading Files ​

Comprehensive guide to reading ZON files and data.

Opening Existing Files ​

The standard workflow for interacting with an existing ZON configuration on disk involves Opening, Reading/Querying, and Closing.

zig
const std = @import("std");
const zon = @import("zon");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // 1. OPEN: Load and parse the file from disk
    var doc = try zon.open(allocator, "config.zon");

    // 2. READ: Query values using path-based access
    if (doc.getString("name")) |name| {
        std.debug.print("Project Name: {s}\n", .{name});
    }

    // 3. CLOSE: Free memory when finished
    doc.close();
}

Automatic Error Handling ​

zon.open handles file access and parsing in one step. It will return errors if the file is missing, inaccessible, or contains invalid ZON syntax.

zig
var doc = zon.open(allocator, "settings.zon") catch |err| {
    std.debug.print("Failed to load config: {any}\n", .{err});
    return err;
};
defer doc.close();

Parse from String ​

zig
const source =
    \\.{
    \\    .name = "myapp",
    \\    .version = "1.0.0",
    \\    .port = 8080,
    \\}
;

var doc = try zon.parse(allocator, source);
defer doc.close();

Read String Values ​

zig
// Basic read
const name = doc.getString("name");
if (name) |n| {
    std.debug.print("Name: {s}\n", .{n});
}

// With default
const version = doc.getString("version") orelse "0.0.0";
std.debug.print("Version: {s}\n", .{version});

Example File (config.zon):

zig
.{
    .name = "myapp",
    .version = "1.0.0",
}

Output:

Name: myapp
Version: 1.0.0

Read Identifier Values ​

Identifiers are values like .name = .my_package:

zig
// Get identifier specifically
if (doc.getIdentifier("name")) |id| {
    std.debug.print("Package: .{s}\n", .{id});
}

// getString also works
if (doc.getString("name")) |s| {
    std.debug.print("Package: {s}\n", .{s});
}

// Check if it's an identifier
if (doc.isIdentifier("name")) {
    std.debug.print("name is an identifier type\n", .{});
}

Example File:

zig
.{
    .name = .my_package,
}

Output:

Package: .my_package
Package: my_package
name is an identifier type

Read Numeric Values ​

Integers ​

zig
const port = doc.getInt("port") orelse 8080;
std.debug.print("Port: {d}\n", .{port});

Large Hex Values (Fingerprints) ​

zig
if (doc.getUint("fingerprint")) |fp| {
    std.debug.print("Fingerprint: 0x{x}\n", .{fp});
}

Example File:

zig
.{
    .port = 8080,
    .fingerprint = 0xee480fa30d50cbf6,
}

Output:

Port: 8080
Fingerprint: 0xee480fa30d50cbf6

Floats ​

zig
const rate = doc.getFloat("rate") orelse 0.0;
std.debug.print("Rate: {d}\n", .{rate});

Read Boolean Values ​

zig
const enabled = doc.getBool("enabled") orelse false;
const debug = doc.getBool("debug") orelse false;

std.debug.print("Enabled: {}\n", .{enabled});
std.debug.print("Debug: {}\n", .{debug});

Example File:

zig
.{
    .enabled = true,
    .debug = false,
}

Output:

Enabled: true
Debug: false

Read Nested Values ​

Use dot notation for nested paths:

zig
const host = doc.getString("database.host") orelse "localhost";
const db_port = doc.getInt("database.port") orelse 5432;
const ssl = doc.getBool("database.ssl.enabled") orelse false;

std.debug.print("Host: {s}\n", .{host});
std.debug.print("Port: {d}\n", .{db_port});
std.debug.print("SSL: {}\n", .{ssl});

Example File:

zig
.{
    .database = .{
        .host = "localhost",
        .port = 5432,
        .ssl = .{
            .enabled = true,
        },
    },
}

Output:

Host: localhost
Port: 5432
SSL: true

Read Arrays ​

Get Array Length ​

zig
if (doc.arrayLen("paths")) |len| {
    std.debug.print("Paths: {d} items\n", .{len});
}

Iterate Array Strings ​

zig
var i: usize = 0;
while (doc.getArrayString("paths", i)) |path| : (i += 1) {
    std.debug.print("  [{d}] {s}\n", .{i, path});
}

Get Specific Index ​

zig
if (doc.getArrayString("paths", 0)) |first| {
    std.debug.print("First path: {s}\n", .{first});
}

if (doc.getArrayInt("numbers", 2)) |num| {
    std.debug.print("Third number: {d}\n", .{num});
}

Example File:

zig
.{
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
    },
    .numbers = .{
        10,
        20,
        30,
    },
}

Output:

Paths: 3 items
  [0] build.zig
  [1] build.zig.zon
  [2] src
First path: build.zig
Third number: 30

Check Values ​

Path Exists ​

zig
if (doc.exists("database.host")) {
    std.debug.print("Database is configured\n", .{});
} else {
    std.debug.print("Using defaults\n", .{});
}

Is Null ​

zig
if (doc.isNull("password")) {
    std.debug.print("Password not set\n", .{});
}

Get Type ​

zig
if (doc.getType("port")) |t| {
    std.debug.print("Type of port: {s}\n", .{t});
}

Possible types: "null", "bool", "int", "float", "string", "identifier", "object", "array"

Read build.zig.zon ​

zig
const source =
    \\.{
    \\    .name = .my_package,
    \\    .version = "0.1.0",
    \\    .fingerprint = 0xee480fa30d50cbf6,
    \\    .minimum_zig_version = "0.15.0",
    \\    .paths = .{
    \\        "build.zig",
    \\        "build.zig.zon",
    \\        "src",
    \\    },
    \\    .dependencies = .{
    \\        .http = .{
    \\            .url = "https://github.com/example/http",
    \\            .hash = "abc123def456",
    \\        },
    \\    },
    \\}
;

var doc = try zon.parse(allocator, source);
defer doc.deinit();

// Package name (identifier)
std.debug.print("Package: .{s}\n", .{doc.getIdentifier("name").?});

// Version
std.debug.print("Version: {s}\n", .{doc.getString("version").?});

// Fingerprint (getUint recommended for u64)
if (doc.getUint("fingerprint")) |fp| {
    std.debug.print("Fingerprint: 0x{x}\n", .{fp});
}

// Paths
std.debug.print("Paths:\n", .{});
var i: usize = 0;
while (doc.getArrayString("paths", i)) |path| : (i += 1) {
    std.debug.print("  - {s}\n", .{path});
}

// Dependencies
if (doc.getString("dependencies.http.url")) |url| {
    std.debug.print("HTTP dep: {s}\n", .{url});
}

Output:

Package: .my_package
Version: 0.1.0
Fingerprint: 0xee480fa30d50cbf6
Paths:
  - build.zig
  - build.zig.zon
  - src
HTTP dep: https://github.com/example/http

Error Handling ​

zig
// Open with error handling
var doc = zon.open(allocator, "config.zon") catch |err| {
    switch (err) {
        error.FileNotFound => {
            std.debug.print("Config file not found, using defaults\n", .{});
            return;
        },
        else => return err,
    }
};
defer doc.deinit();

// Parse with error handling
var parsed = zon.parse(allocator, source) catch |err| {
    std.debug.print("Parse error: {}\n", .{err});
    return;
};
defer parsed.deinit();

File Utilities ​

zig
// Check if file exists before opening
if (zon.fileExists("config.zon")) {
    var doc = try zon.open(allocator, "config.zon");
    defer doc.deinit();
    // ...
}

Released under the MIT License.