jassielof/toonz
A Zig implementation of the TOON (Token-Oriented Object Notation) format.
A Zig implementation of the TOON (Token-Oriented Object Notation) format - a compact, human-readable encoding of the JSON data model designed for LLM input efficiency.
Official Specification (v3.0) | Format Overview
TOON combines YAML's indentation-based structure with CSV-style tabular arrays to achieve significant token reduction while maintaining lossless JSON compatibility. It's particularly efficient for uniform arrays of objects, achieving ~40% fewer tokens than JSON in mixed-structure benchmarks while improving LLM accuracy.
See the official TypeScript implementation and the full specification for complete format details.
Encoding/Decoding: Full JSON ↔ TOON ↔ ZON conversion
std.json.Value ↔ TOON string serializationstd.json.Value or custom Zig typesData Types: Complete JSON data model support
Tabular Arrays: Optimized encoding for uniform object arrays
[N]{field1,field2,...}: header with row data, (default), tab \t, pipe |[N,] / [N\t] / [N|]Key Folding (key_folding = .safe):
data.metadata.items instead of nested indentationflatten_depth optionPath Expansion (expand_paths = .safe):
data.items → { "data": { "items": ... } }Strict Mode Validation (strict = true):
[N] countsError Handling:
max_depth limits (default: 256)Smart command-line interface with automatic format detection:
# Auto-detect based on file extension
toonz input.json # → TOON output
toonz data.toon # → JSON output
toonz config.zon # → TOON output
# Explicit commands
toonz serialize input.json # JSON/ZON → TOON
toonz deserialize data.toon # TOON → JSON
# Output format control
toonz data.toon --json # → JSON
toonz data.toon --zon # → ZON (Zig Object Notation)
toonz input.json --toon # → TOON
# File I/O
toonz input.json -o output.toon
echo '{"key":"value"}' | toonz serialize
# Format command (coming soon)
toonz format data.toon # Reformat TOON file
Supported modes:
.json, .toon, .zon)serialize, deserialize, format)--json, --zon, --toon)# Clone the repository
git clone --recursive https://github.com/jassielof/toonz
cd toonz
# Build
zig build
# Run tests
zig build test
# Install
zig build install
const std = @import("std");
const toonz = @import("toonz");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Encode: JSON → TOON
const json_string =
\\{
\\ "users": [
\\ {"id": 1, "name": "Alice", "active": true},
\\ {"id": 2, "name": "Bob", "active": false}
\\ ]
\\}
;
const json_value = try std.json.parseFromSlice(
std.json.Value,
allocator,
json_string,
.{}
);
defer json_value.deinit();
const toon_output = try toonz.serialize.stringify(
json_value.value,
.{ .delimiter = ',' }, // Options: delimiter, key_folding, etc.
allocator
);
defer allocator.free(toon_output);
std.debug.print("{s}\n", .{toon_output});
// Output:
// users[2]{id,name,active}:
// 1,Alice,true
// 2,Bob,false
// Decode: TOON → JSON
const toon_input =
\\users[2]{id,name,active}:
\\ 1,Alice,true
\\ 2,Bob,false
;
const parsed = try toonz.Parse(std.json.Value).parse(
allocator,
toon_input,
.{ .strict = true, .expand_paths = .off }
);
defer parsed.deinit();
// Use parsed.value as std.json.Value
}
Core Implementation:
[N]{fields}: formatAdvanced Features:
,, tab \t, pipe |[N,] / [N\t] / [N|]key_folding = .safe) with configurable depthexpand_paths = .safe) for dotted keysmax_depth limit)CLI & Tooling:
serialize, deserialize)--json, --zon, --toon)Testing:
Spec Compliance:
spec/tests/fixtures/encode/spec/tests/fixtures/decode/Performance:
Delimiter Intelligence:
Developer Experience:
zig build docs)Feature Parity:
| Feature | toonz (Zig) | @toon-format/toon (TS) | Notes |
|---|---|---|---|
| Core encode/decode | ✅ | ✅ | Full compatibility |
| Tabular arrays | ✅ | ✅ | Same format |
Delimiters (, \t |) |
✅ | ✅ | All supported |
| Key folding | ✅ | ✅ | Safe mode |
| Path expansion | ✅ | ✅ | Safe mode |
| Strict validation | ✅ | ✅ | Array lengths, fields |
| Streaming API | ⚠️ | ✅ | Basic support, needs enhancement |
| Format command | 🚧 | ✅ | In progress |
| CLI stats/benchmarks | ❌ | ✅ | Planned |
Zig-Specific Features:
Current:
Design Choices:
max_depth default is 256 (prevents stack overflow on malicious input)Tests use the official spec fixtures from the spec/ submodule:
# Run all tests
zig build test
# Tests include:
# - Basic encode/decode round-trips
# - JSON compatibility
# - Spec fixture conformance (spec/tests/fixtures/)
# - Reference implementation comparison
Test fixtures are organized by:
spec/tests/fixtures/encode/ - Encoding (JSON → TOON) testsspec/tests/fixtures/decode/ - Decoding (TOON → JSON) testsEach fixture tests specific features: tabular arrays, delimiters, key folding, edge cases, etc.
const toonz = @import("toonz");
// Encode std.json.Value to TOON
const toon_string = try toonz.serialize.stringify(
json_value, // std.json.Value
.{ // Options
.indent = 2,
.delimiter = ',',
.key_folding = .safe,
.flatten_depth = null, // No limit
},
allocator
);
// Encode to writer (streaming)
try toonz.serialize.stringifyToWriter(
json_value,
options,
writer,
allocator
);
// Main API struct
const Stringify = toonz.Stringify;
const result = try Stringify.value(json_value, options, allocator);
Options:
indent: u64 - Number of spaces for indentation (default: 2)delimiter: ?u8 - Delimiter for arrays: ,, \t, | (default: ,)key_folding: enum { off, safe } - Collapse single-key chains (default: .off)flatten_depth: ?u64 - Max folding depth, null = unlimited (default: null)const toonz = @import("toonz");
// Parse TOON to std.json.Value
const parsed = try toonz.Parse(std.json.Value).parse(
allocator,
toon_input, // []const u8
.{ // Options
.indent = null, // Auto-detect
.strict = true,
.expand_paths = .safe,
.max_depth = 256,
}
);
defer parsed.deinit();
// Use: parsed.value
// Parse to custom Zig type
const MyStruct = struct {
name: []const u8,
age: u32,
};
const parsed_struct = try toonz.Parse(MyStruct).parse(
allocator,
toon_input,
.{}
);
defer parsed_struct.deinit();
Options:
indent: ?usize - Expected indentation, null = auto-detect (default: null)strict: ?bool - Enforce strict validation (default: true)expand_paths: enum { off, safe } - Expand dotted keys (default: .off)max_depth: usize - Max nesting depth for safety (default: 256)The internal Value type represents TOON/JSON values:
const toonz = @import("toonz");
const Value = toonz.Value;
const value = Value{
.object = std.StringHashMap(Value).init(allocator),
};
// Types: .null, .bool, .number, .string, .array, .object
switch (value) {
.object => |obj| // std.StringHashMap(Value)
.array => |arr| // std.ArrayList(Value)
.string => |s| // []const u8
.number => |n| // f64
.bool => |b| // bool
.null => // void
}
// Cleanup
value.deinit(allocator);
toonz/
├── src/
│ ├── lib/ # Core library
│ │ ├── root.zig # Public API exports
│ │ ├── Value.zig # Value type definition
│ │ ├── serialize/ # Encoding (JSON → TOON)
│ │ │ ├── root.zig
│ │ │ ├── Options.zig
│ │ │ ├── encoders.zig
│ │ │ ├── folding.zig
│ │ │ └── ...
│ │ ├── deserialize/ # Decoding (TOON → JSON)
│ │ │ ├── root.zig
│ │ │ ├── Parse.zig
│ │ │ ├── Scanner.zig
│ │ │ ├── expand.zig
│ │ │ └── types/
│ │ └── format/ # Formatting/prettification
│ ├── cli/ # Command-line tool
│ │ ├── main.zig
│ │ └── commands/
│ │ ├── serialize.zig
│ │ ├── deserialize.zig
│ │ └── format.zig
│ └── tests/ # Test suite
│ ├── suite.zig
│ ├── basic.zig
│ ├── json.zig
│ └── spec/ # Spec fixture tests
├── spec/ # Official spec submodule
│ ├── SPEC.md
│ └── tests/fixtures/
├── js/ # Official TS implementation
│ └── packages/
├── build.zig
└── README.md
This implementation follows the TOON Specification v3.0.
Key spec sections implemented:
[N]{fields}: format)Differences from spec:
max_depth safety limit (not in spec, prevents stack overflow)Contributions are welcome! This implementation aims for full spec compliance.
Priority areas:
Development:
# Clone with submodules
git clone --recursive https://github.com/jassielof/toonz
cd toonz
# Build and test
zig build
zig build test
# Generate docs
zig build docs
Please ensure tests pass before submitting PRs.
MIT License - See LICENSE file for details.
js/ submodule)