A quick reference for the Zig API. The Zig source remains the source of truth.
The owning entry points return a value with deinit. Call it exactly once.
typed.parsereturnsParsed(T).dom.parseanddom.parseIntoreturnDocument.dom.Document.toMutreturnsDocumentMut.
typed.parseBorrowed, typed.parseInto and dom.parseBufferSize have no owner
to release.
var document = try jsonz.dom.parse(allocator, input, .{});
defer document.deinit();Invalid:
var document = try jsonz.dom.parse(allocator, input, .{});
var other = document;
document.deinit();
other.deinit(); // double releaseValues returned by views and strings decoded in place borrow the owner's storage.
Nodeand the[]const u8returned bytoStringare only valid while theDocumentlives.NodeMutand the[]const u8returned bytoStringare only valid while theDocumentMutlives. ADocumentMutowns its nodes and strings, so it borrows nothing from the caller.parseBorrowedborrows unescaped strings directly from the input, which must outlive the value.parseIntoborrows caller-provided storage, which must outlive the value.
DOM string escapes are decoded in place, so dom.parse copies the input into
the document; the caller's buffer can be freed right after parsing.
Zig error sets are explicit. AccessError and PointerError cover node access;
ParseError covers parsing; serialization returns allocator and IO errors.
| Name | Kind | Purpose |
|---|---|---|
typed.Parsed(T) |
owner | Owning typed parse result. |
typed.ParseOptions |
struct | Typed parse options. |
typed.ParseError |
error set | Typed parse errors. |
typed.SerializeOptions |
struct | Typed serialization options. |
dom.Document |
owner | Owns parsed DOM storage. |
dom.Node |
view | A borrowed JSON node. |
dom.Node.ObjectIterator |
view | Iterator over object fields. |
dom.Node.ArrayIterator |
view | Iterator over array elements. |
dom.Node.ObjectEntry |
view | One key/value pair. |
dom.DocumentMut |
owner | Owns an editable DOM. |
dom.NodeMut |
view | A borrowed handle to one node of a DocumentMut. |
dom.NodeMut.ObjectIterator |
view | Iterator over object fields. |
dom.NodeMut.ArrayIterator |
view | Iterator over array elements. |
dom.NodeMut.ObjectEntry |
view | One key/value pair. |
dom.Kind |
enum | null, bool, number, string, array, object. |
dom.NumberType |
enum | Numeric targets for toNumber and asNumber. |
dom.AccessError |
error set | Node access and conversion errors. |
dom.PointerError |
error set | JSON Pointer resolution errors. |
dom.MutateError |
error set | Structural edit errors. |
dom.ParseOptions |
struct | DOM parse options. |
dom.ParseError |
error set | DOM parse errors. |
dom.WriteOptions |
struct | DOM serialization options. |
dom.DocumentMut.patch.Error |
error set | Patch application errors. |
dom.DocumentMut.patch.Options |
struct | Patch parse options. |
diagnostic.Diagnostic |
struct | One syntax error and where it is. |
diagnostic.Span |
struct | A byte range of the input. |
diagnostic.Note |
struct | Extra context for a report. |
diagnostic.Problem |
union | Everything that can be wrong. |
diagnostic.Options |
struct | What counts as valid JSON. |
diagnostic.ReportOptions |
struct | How a report looks. |
| API | Returns | Purpose |
|---|---|---|
typed.parse(T, allocator, input, options) |
ParseError!Parsed(T) |
Parse into an owning result. |
typed.parseBorrowed(T, allocator, input, options) |
ParseError!T |
Parse, borrowing strings from input when possible. |
typed.parseInto(T, buffer, input, options) |
ParseError!T |
Parse using caller-provided storage. |
typed.toSlice(allocator, value, options) |
![]u8 |
Serialize a Zig value. |
typed.toWriter(writer, value, options) |
!void |
Serialize straight to a writer. |
parseBorrowed and parseInto return the value directly; the caller keeps the
borrowed storage alive.
| API | Purpose |
|---|---|
.value |
The decoded value. |
.deinit() |
Release the input copy and any fallback allocation. |
.toSlice(allocator, options) |
Serialize .value. |
.toWriter(writer, options) |
Serialize .value. |
T may define jsonzDeserialize / jsonzSerialize hooks for custom
representations. A deserialize hook receives an allocator and a deserializer;
a serialize hook receives a serializer. Both expose methods for reading or
writing JSON values.
typed.ParseOptions:
| Field | Default | Description |
|---|---|---|
ignore_unknown_fields |
false |
Skip JSON fields not declared by T. |
max_depth |
256 |
Reject input nested deeper than this. |
typed.SerializeOptions:
| Field | Default | Description |
|---|---|---|
pretty |
false |
Format with indentation and line breaks. |
indent |
2 |
Spaces per level when pretty is set. |
| API | Returns | Purpose |
|---|---|---|
dom.parse(allocator, input, options) |
ParseError!Document |
Parse arbitrary JSON. |
dom.parseInto(storage, input, options) |
ParseError!Document |
Parse using caller-provided storage. |
dom.parseBufferSize(input_len, options) |
usize |
Storage size required by parseInto. |
Document.toMut(allocator) |
Allocator.Error!DocumentMut |
Copy into an editable document. |
dom.parseMut(allocator, input, options) |
ParseError!DocumentMut |
Parse straight into an editable document. |
dom.ParseOptions:
| Field | Default | Description |
|---|---|---|
allow_comments |
false |
Accept // and /* ... */ comments. |
allow_trailing_commas |
false |
Accept a comma before ] or }. |
dom.WriteOptions:
| Field | Default | Description |
|---|---|---|
pretty |
false |
Format with indentation and line breaks. |
Document forwards the root node's accessors, so document.field(...) and
document.root().field(...) are the same call.
| API | Purpose |
|---|---|
deinit() |
Release the DOM storage. |
root() |
The root Node. |
kind() |
Kind of the root. |
isNull(), isBool(), isNumber(.xxx), isString(), isArray(), isObject() |
Root type checks. |
len() |
Field or element count of a root container. |
toBool(), toNumber(.xxx), toString() |
Strict root conversion. |
asBool(), asNumber(.xxx), asString() |
Optional root conversion. |
get(key), field(key) |
Root object member. |
getAt(index), at(index) |
Root array element. |
objectIterator(), arrayIterator() |
Root container iteration. |
ptrGet(ptr), ptrGetFmt(fmt, args), ptrGetDyn(ptr) |
Root JSON Pointer. |
toSlice(allocator, options), toWriter(writer, options) |
Serialize the root. |
toMut(allocator) |
Copy the document into a new DocumentMut. |
For caller-provided storage:
const size = jsonz.dom.parseBufferSize(input.len, .{});
const storage = try allocator.alloc(u8, size);
defer allocator.free(storage);
var document = try jsonz.dom.parseInto(storage, input, .{});
defer document.deinit();A Node is the only node type; object, array and scalar operations live on
it directly.
Access:
| Method | Returns | Failure |
|---|---|---|
kind() |
Kind |
— |
isNull(), isBool(), isString(), isArray(), isObject() |
bool |
— |
isNumber(.xxx) |
bool |
Not convertible. |
len() |
AccessError!usize |
Not a container. |
toBool() |
AccessError!bool |
Not a boolean. |
toNumber(.xxx) |
AccessError!T |
Not numeric, or out of range. |
toString() |
AccessError![]const u8 |
Not a string. |
asBool() |
?bool |
Not a boolean. |
asNumber(.xxx) |
?T |
Not numeric, or out of range. |
asString() |
?[]const u8 |
Not a string. |
Containers:
| Method | Returns | Failure |
|---|---|---|
get(key) |
?Node |
Not an object, or absent. |
field(key) |
AccessError!Node |
Not an object, or absent. |
getAt(index) |
?Node |
Not an array, or out of range. |
at(index) |
AccessError!Node |
Not an array, or out of range. |
objectIterator() |
AccessError!ObjectIterator |
Not an object. |
arrayIterator() |
AccessError!ArrayIterator |
Not an array. |
Iteration:
var fields = try view.objectIterator();
while (fields.next()) |entry| {
entry.key; // []const u8
entry.value; // Node
}
var elements = try view.arrayIterator();
while (elements.next()) |element| {
_ = element; // Node
}Serialization:
| Method | Purpose |
|---|---|
toSlice(allocator, options) |
Serialize to a new slice owned by allocator. |
toWriter(writer, options) |
Serialize straight to a writer. |
Integer-to-floating-point conversion may lose precision.
DocumentMut is an independent, editable DOM. Get one with
Document.toMut(allocator): the copy is deep, the original Document stays
valid and unchanged, and the two share nothing. Editing is in place and does
not re-parse or re-serialize anything.
Build one from scratch with init(allocator, .object) or .array: a new
document's root is a container, and root() is the node to fill. The root is
an ordinary node, so a document that holds something else - a scalar, null, or
a subtree built detached - is made by replacing it, e.g. with
root().replaceNumber(value) or root().replace(detached).
| API | Purpose |
|---|---|
init(allocator, kind) |
An empty document whose root is .object or .array. |
parse(allocator, input, options) |
Parse JSON straight into a mutable document. |
applyPatch(text, options) |
Apply an RFC 6902 JSON Patch, atomically. |
clone(allocator, source) |
Deep-copy another document. |
deinit() |
Release the node and string storage. |
root() |
The root NodeMut. |
toSlice(allocator, options), toWriter(writer, options) |
Serialize the root. |
toDocument(allocator) |
Copy the document into an independent, read-only Document. |
ptrGet(ptr), ptrGetFmt(fmt, args), ptrGetDyn(ptr) |
Root JSON Pointer, with the same semantics as Document. |
newNull(), newBool(value), newNumber(value), newString(value), newArray(), newObject() |
Create a detached node to attach later. |
toDocument(allocator) creates an independent read-only Document. The copy
stays valid after the mutable document is freed, and exposes only read operations.
var mutable = try jsonz.dom.parseMut(allocator, input, .{});
defer mutable.deinit();
try mutable.root().addString("state", "done");
var frozen = try mutable.toDocument(allocator);
defer frozen.deinit();
// `frozen` is a plain `Document`: hand out `&frozen` and nothing can change it.newNumber accepts Zig integers and floats. Values that cannot be represented
in JSON report error.OutOfRange.
NodeMut reads exactly like Node: kind, is*, len, get/field,
getAt/at, to*/as*, objectIterator/arrayIterator, ptrGet /
ptrGetFmt / ptrGetDyn, and toSlice/toWriter all exist with the same
names, arguments, and errors. It adds the editing methods below.
Detached nodes are what editors attach. Create them with the DocumentMut.new*
methods. Editing methods require a node from the same DocumentMut; a node from a
different document reports error.DifferentStorage.
| Method | Effect |
|---|---|
replaceNull() |
Set this node to null, keeping its position. |
replaceBool(value) |
Set this node to a boolean, keeping its position. |
replaceNumber(value) |
Set this node to a number, keeping its position. |
replaceString(value) |
Set this node to a string, keeping its position. |
remove() |
Detach this node. An object member is removed with its key; the root and already-detached nodes are left alone. |
replace(value) |
Splice a detached node into this node's position; this node becomes detached. |
copyFrom(source) |
Deep-copy source into this node, keeping this node's position. source may belong to another document. |
addField(key, value) |
Append an object member. |
addNull(key) |
Append an object member whose value is null. |
addBool(key, value) |
Append an object member whose value is a boolean. |
addNumber(key, value) |
Append an object member whose value is a number. |
addString(key, value) |
Append an object member whose value is a string. |
append(value) |
Append an array element. |
appendNull() |
Append a null array element. |
appendBool(value) |
Append a boolean array element. |
appendNumber(value) |
Append a number array element. |
appendString(value) |
Append a string array element. |
insertAt(index, value) |
Insert an array element at index; index == len appends. |
Convenience methods create the value and attach it in one call:
| Methods | Equivalent operation |
|---|---|
addNull, addBool, addNumber, addString |
new* + addField |
appendNull, appendBool, appendNumber, appendString |
new* + append |
| Operation | Behavior |
|---|---|
remove() |
Detaches the node; removing an object value also removes its key. Removing the root does nothing. |
replace*() / replace() |
Keeps the node's position and changes its value. |
copyFrom() |
Copies a subtree, including from another document. |
| Duplicate object key | Appends another member; lookup returns the first one. |
| Detached node | Remains valid and can be attached again. |
| Error | Cause |
|---|---|
AlreadyAttached |
The node is already in a tree, including the target itself. |
DifferentStorage |
The node belongs to another document. |
WouldCycle |
The operation would create a cycle. |
UnexpectedType |
The target is not the required container kind. |
OutOfBounds |
insertAt is past the end of the array. |
OutOfRange |
A number cannot be represented in JSON. |
| API | Returns | Purpose |
|---|---|---|
ptrGet("/user/id") |
PointerError!Node |
Comptime RFC 6901 pointer. |
ptrGetFmt("/users/{}/id", .{index}) |
PointerError!Node |
Comptime format plus runtime arguments. |
ptrGetDyn(ptr) |
PointerError!Node |
A complete pointer known only at runtime. |
ptrGet checks syntax, escapes and UTF-8 at compile time. ptrGetFmt uses
textual interpolation and does not escape interpolated values; write / and ~
as ~1 and ~0 when they belong to an object key.
A token is an object member or an array index depending on the node it meets, as
RFC 6901 requires. ~1 decodes to
/, ~0 to ~, and object keys match by exact code point without Unicode
normalization. Duplicate member names resolve to the first match.
The RFC 6901 URI fragment representation (#/user/id) is not implemented;
ptrGetDyn accepts the JSON string representation only.
RFC 6902 JSON Patch. A patch is a
scripted sequence of the NodeMut edits above, so it lives on the mutable
document rather than in a module of its own.
| API | Returns | Purpose |
|---|---|---|
DocumentMut.applyPatch(text, options) |
Error!void |
Parse and apply a patch, atomically. |
DocumentMut.patch.apply(document, text, options) |
Error!void |
The same, as a free function. |
DocumentMut.patch.applyOps(document, ops) |
Error!void |
Apply an already parsed patch in place. |
A patch is a JSON array of operation objects. Each names its target with an
RFC 6901 JSON Pointer and they are applied in order; the six operations are
add, remove, replace, move, copy, and test.
var mutable = try jsonz.dom.parseMut(allocator, input, .{});
defer mutable.deinit();
try mutable.applyPatch(patch_text, .{});applyPatch is atomic: the operations run on a private deep copy that is
committed only when the whole patch succeeds, so a failure leaves the document
exactly as it was. That copy is the price of the guarantee; applyOps edits the document
in place and keeps the operations that ran before a failure.
addsets an object member, inserts an array element (-appends), and replaces the whole document when the path is empty.removeneeds its target to exist, and removes an object member with its key.replaceneeds its target to exist. A path of""replaces the document.moveremovesfromand then adds atpath, so an array index is read after the removal. The destination must not be inside the moved value (error.InvalidMove).copydeep-copiesfromintopath; copying into the source's own child is allowed, and the copy is independent.testcompares by value: numbers compare numerically (1equals1.0) and exactly for integers, object members compare as an unordered set, and array elements compare in order. A mismatch reportserror.TestFailed.
DocumentMut.patch.Options:
| Field | Default | Description |
|---|---|---|
parse |
.{} |
Parse options for the patch document, e.g. comments. |
| API | Returns | Purpose |
|---|---|---|
diagnostic.diagnose(input, options) |
?Diagnostic |
The first syntax error, or null. |
diagnostic.isValid(input, options) |
bool |
Whether the input is valid. |
diagnostic.print(input, options) |
!void |
Write the report to standard error; silent when valid. |
diagnostic.printWith(input, options, terminal) |
!void |
Write the report to a terminal; silent when valid. |
diagnostic.toSlice(allocator, input, options) |
!?[]const u8 |
The report as a new slice, or null when valid. |
Nothing here needs a parser, a schema, or a mutable input. print and
printWith stream the report, so nothing is allocated and its size is
unbounded; toSlice materialises it. Syntax is checked, not a schema, and only
the first error is reported.
try jsonz.diagnostic.print(input, .{ .source_name = "config.json" });config.json:3:18: error: expected ',' or ']', found '"'
1 | {
2 | "name": "jsonz",
3 | "tags": ["zig" "json"],
| ^
4 | "count": 3
5 | }diagnostic.Options, used by isValid and diagnose:
| Field | Default | Description |
|---|---|---|
allow_comments |
false |
Accept C-style comments. |
allow_trailing_commas |
false |
Accept a trailing comma. |
diagnostic.ReportOptions, used by print, printWith and toSlice:
| Field | Default | Description |
|---|---|---|
check |
.{} |
Check options for this document. |
source_name |
"<input>" |
Name shown in the header. |
context_lines |
3 |
Source lines shown above and below. |
max_line_width |
200 |
Cut longer lines around the problem; 0 shows all. |
Colour is not an option: it belongs to the stream. print colours standard
error whenever it is a terminal that takes escape codes, honouring NO_COLOR
and CLICOLOR_FORCE; printWith writes to the std.Io.Terminal you hand it,
so that stream decides; toSlice returns plain text.
AccessError:
| Value | Cause |
|---|---|
UnexpectedType |
The node is not the requested JSON kind. |
OutOfRange |
A number does not fit the requested or the stored type. |
MissingField |
The object member is absent. |
OutOfBounds |
The array index is past the end. |
PointerError is AccessError plus:
| Value | Cause |
|---|---|
InvalidPointer |
Malformed pointer, invalid escape, or invalid UTF-8. |
InvalidArrayIndex |
A token that is not an RFC 6901 array index. |
PointerTooLong |
A formatted pointer is too long. |
MutateError is Allocator.Error plus AccessError plus:
| Value | Cause |
|---|---|
AlreadyAttached |
The node is already linked into a tree. |
DifferentStorage |
The node belongs to another document. |
WouldCycle |
The attach would make the node its own descendant. |
typed.ParseError:
| Value | Cause |
|---|---|
UnexpectedToken |
The input does not match the target type. |
UnexpectedEof |
The input ended early. |
InvalidNumber |
A number is malformed or overflows its target. |
InvalidEscape |
A string escape is malformed. |
InvalidControlCharacter |
A raw control byte appears in a string. |
InvalidUtf8 |
A string is not valid UTF-8. |
InvalidUnicode |
A \u escape is not a valid code point. |
MaxDepthExceeded |
Nesting passes max_depth. |
WrongType |
The JSON kind does not match T. |
UnknownField |
An object field has no match in T. |
MissingField |
A declared field is absent. |
TrailingData |
Bytes remain after the value. |
OutOfMemory |
Allocation failed. |
dom.ParseError:
| Value | Cause |
|---|---|
InvalidJson |
The input is not valid JSON. |
OutOfMemory |
Allocation failed. |
DocumentMut.patch.Error is dom.ParseError, dom.PointerError and
dom.MutateError
combined, plus:
| Value | Cause |
|---|---|
InvalidPatch |
The patch is not an array of valid operation objects. |
InvalidTarget |
The operation is not defined for the target location. |
InvalidMove |
move would move a value into its own child. |
TestFailed |
A test operation found a different value. |