Skip to main content

Overview

The std.json module provides comprehensive JSON parsing and serialization capabilities conforming to RFC 8259. It offers both low-level token-based APIs and high-level functions for parsing JSON into Zig types.

High-Level API

parseFromSlice

pub fn parseFromSlice(
    comptime T: type, 
    allocator: Allocator, 
    source: []const u8, 
    options: ParseOptions
) !Parsed(T)
Parses a JSON document from a string slice into a Zig value.
T
type
The Zig type to parse into.
allocator
Allocator
Allocator for dynamic memory (strings, arrays, etc.).
source
[]const u8
JSON string to parse.
options
ParseOptions
Parsing configuration options.
return
Parsed(T)
A structure containing the parsed value and arena allocator. Call .deinit() to free.
Example:
const std = @import("std");

const User = struct {
    name: []const u8,
    age: u32,
    active: bool = true,
};

var parsed = try std.json.parseFromSlice(
    User,
    allocator,
    \"{\"name\": \"Alice\", \"age\": 30}",
    .{}
);
defer parsed.deinit();

// Access the parsed value
const user = parsed.value;
std.debug.print("Name: {s}, Age: {}\n", .{user.name, user.age});

parseFromSliceLeaky

pub fn parseFromSliceLeaky(
    comptime T: type,
    allocator: Allocator,
    source: []const u8,
    options: ParseOptions
) !T
Parses JSON without managing cleanup. Caller must free all allocations manually.

stringify

pub fn stringify(
    value: anytype,
    options: StringifyOptions,
    writer: *std.Io.Writer
) !void
Serializes a Zig value as JSON. Example:
const User = struct { name: []const u8, age: u32 };
const user = User{ .name = "Bob", .age = 25 };

var list = std.ArrayList(u8).init(allocator);
defer list.deinit();

var writer = std.Io.Writer.Allocating.init(allocator);
defer writer.deinit();

try std.json.stringify(user, .{}, &writer.writer);
const json_string = writer.written();
// json_string is "{\"name\":\"Bob\",\"age\":25}"

Dynamic JSON Values

Value

pub const Value = union(enum) {
    null,
    bool: bool,
    integer: i64,
    float: f64,
    number_string: []const u8,
    string: []const u8,
    array: Array,
    object: ObjectMap,
};
Represents a dynamically-typed JSON value for runtime inspection. Example:
var parsed = try std.json.parseFromSlice(
    std.json.Value,
    allocator,
    \"{\"key\": [1, 2, 3], \"nested\": {\"flag\": true}}",
    .{}
);
defer parsed.deinit();

const obj = parsed.value.object;
const array = obj.get("key").?.array;
const first = array.items[0].integer; // 1

const nested = obj.get("nested").?.object;
const flag = nested.get("flag").?.bool; // true

ObjectMap

pub const ObjectMap = std.StringArrayHashMapUnmanaged(Value);
A map type for JSON objects, preserving insertion order.

Array

pub const Array = std.ArrayListUnmanaged(Value);
A dynamic array for JSON arrays.

Low-Level Scanner API

Scanner

pub const Scanner = struct {
    pub fn initCompleteInput(allocator: Allocator, source: []const u8) Scanner;
    pub fn next(self: *Scanner) !Token;
    pub fn deinit(self: *Scanner) void;
};
Low-level token scanner for streaming JSON parsing. Example:
var scanner = std.json.Scanner.initCompleteInput(
    allocator,
    \"{\"foo\": 123}\n"
);
defer scanner.deinit();

const tok1 = try scanner.next(); // .object_begin
const tok2 = try scanner.next(); // .string with value "foo"
const tok3 = try scanner.next(); // .number with value "123"
const tok4 = try scanner.next(); // .object_end
const tok5 = try scanner.next(); // .end_of_document

Token

pub const Token = union(enum) {
    object_begin,
    object_end,
    array_begin,
    array_end,
    string: []const u8,
    number: []const u8,
    true,
    false,
    null,
    end_of_document,
};
Represents a single JSON token.

Stringify API

Stringify

pub const Stringify = struct {
    writer: *std.Io.Writer,
    options: Options,
    
    pub const Options = struct {
        whitespace: Whitespace = .minified,
        
        pub const Whitespace = union(enum) {
            minified,
            indent_1,
            indent_2,
            indent_3,
            indent_4,
            indent_tab,
        };
    };
};
Low-level JSON writer for manual control. Methods:
pub fn beginObject(self: *Stringify) !void;
pub fn endObject(self: *Stringify) !void;
pub fn beginArray(self: *Stringify) !void;
pub fn endArray(self: *Stringify) !void;
pub fn objectField(self: *Stringify, name: []const u8) !void;
pub fn write(self: *Stringify, value: anytype) !void;
Example:
var out = std.Io.Writer.Allocating.init(allocator);
defer out.deinit();

var writer = std.json.Stringify{
    .writer = &out.writer,
    .options = .{ .whitespace = .indent_2 },
};

try writer.beginObject();
try writer.objectField("name");
try writer.write("Alice");
try writer.objectField("items");
try writer.beginArray();
try writer.write(1);
try writer.write(2);
try writer.endArray();
try writer.endObject();

const json = out.written();
// {
//   "name": "Alice",
//   "items": [
//     1,
//     2
//   ]
// }

Parse Options

ParseOptions

pub const ParseOptions = struct {
    allocate: AllocWhen = .alloc_always,
    duplicate_field_behavior: DuplicateFieldBehavior = .use_first,
    ignore_unknown_fields: bool = false,
    max_value_len: ?usize = default_max_value_len,
    
    pub const AllocWhen = enum {
        alloc_always,
        alloc_if_needed,
    };
    
    pub const DuplicateFieldBehavior = enum {
        use_first,
        @"error",
        use_last,
    };
};
allocate
AllocWhen
Controls when to allocate for strings. .alloc_always copies all strings; .alloc_if_needed may reference source.
duplicate_field_behavior
DuplicateFieldBehavior
How to handle duplicate object keys.
ignore_unknown_fields
bool
If true, ignore JSON fields that don’t match struct fields.
max_value_len
?usize
Maximum length for string/number tokens.

Validation

validate

pub fn validate(source: []const u8) !void
Validates that a string contains well-formed JSON. Example:
try std.json.validate("{\"valid\": true}");
// Returns successfully

std.json.validate("{invalid}") catch |err| {
    // err is std.json.Error.SyntaxError
};

Formatting Helper

fmt

pub fn fmt(value: anytype, options: Stringify.Options) Formatter(@TypeOf(value))
Returns a formatter that serializes a value using std.fmt integration. Example:
const value = .{ .x = 10, .y = 20 };
std.debug.print("JSON: {f}\n", .{std.json.fmt(value, .{})});
// Prints: JSON: {"x":10,"y":20}

Error Types

Error

pub const Error = error{
    UnexpectedEndOfInput,
    InvalidNumber,
    InvalidEscape,
    InvalidUnicodeEscape,
    InvalidTopLevel,
    SyntaxError,
    BufferUnderrun,
};

ParseError

pub const ParseError = error{
    UnexpectedToken,
    InvalidNumber,
    Overflow,
    InvalidEnumTag,
    DuplicateField,
    UnknownField,
    MissingField,
    LengthMismatch,
} || Error;

Build docs developers (and LLMs) love