Micru Blog Logo

TOON: A Format Built for the Age of LLMs

A deep look at TOON (Token-Oriented Object Notation): how it works, what makes it different from JSON, YAML, CSV, and XML, and why it matters for LLM workflows.

Micru 15 min read

If you’ve ever passed a large JSON payload into a language model prompt, you’ve already felt the problem TOON is trying to solve. You had a dataset (maybe a list of records, maybe a nested config) and you needed the model to reason about it. So you serialized it to JSON, dropped it in the prompt, and burned a few hundred tokens just on curly braces, repeated field names, and structural punctuation.

TOON (Token-Oriented Object Notation) is a format designed to address that directly. It represents the same JSON data model but in a significantly more compact way, particularly for the kind of data that shows up most often in real-world LLM workflows: uniform arrays of objects, structured records, nested configurations.

This article walks through how TOON actually works: the syntax, the design decisions, how it compares to the formats you’re already using, and where it fits (and doesn’t) in a modern data pipeline.

You can try it right now at toon.micru.org. This article focuses on the format itself.


The Problem With JSON for LLMs

Before getting into TOON’s syntax, it helps to understand what makes JSON expensive in a prompt context.

Take a simple array of employee records:

[
  { "id": 1, "name": "Alice", "department": "Engineering", "salary": 95000 },
  { "id": 2, "name": "Bob", "department": "Marketing", "salary": 82000 },
  { "id": 3, "name": "Carol", "department": "Engineering", "salary": 101000 }
]

Every single row repeats the same four field names: id, name, department, salary. In a dataset with a hundred rows, those field names appear a hundred times each. The actual data (the numbers and strings) is maybe 30% of the token count. The rest is structure.

This isn’t a bug in JSON. JSON was designed for machine-to-machine communication where verbosity is a non-issue. But for prompts, every token counts. You’re paying for those repeated keys. You’re also bumping up against context window limits on larger datasets, or simply making the model’s job harder by burying the signal in structural noise.

What TOON Does Differently

TOON’s core idea is straightforward: for arrays where every element has the same shape, declare the structure once and stream the data as rows. It’s the same insight that makes CSV efficient, but with explicit structure that goes beyond what CSV can represent.

Here’s the same employee array in TOON:

employees[3]{id,name,department,salary}:
  1,Alice,Engineering,95000
  2,Bob,Marketing,82000
  3,Carol,Engineering,101000

The header line employees[3]{id,name,department,salary}: declares everything you need to know about the array: the key it’s assigned to (employees), the number of elements ([3]), and the field names in order ({id,name,department,salary}). Each subsequent line is a row: values in the same order as the header, separated by commas. Field names appear once. The actual data gets the full token budget.

The token savings on this small example are already meaningful: around 50% compared to formatted JSON, and around 25% compared to JSON compact. On larger datasets with more fields, the savings grow further.

Three Ways to Represent Arrays

TOON doesn’t force a single format on all arrays. It picks the representation based on what the data actually looks like.

Tabular format (the example above) applies when all elements in an array are objects with the same keys and primitive values. This is TOON’s primary contribution: the case where the savings are largest and the most common case in real-world datasets.

Primitive (inline) format applies when an array contains only primitive values: strings, numbers, booleans, or null. These are written inline, separated by the active delimiter:

tags[3]: admin,ops,dev
scores[4]: 98,87,72,91

No header needed. The field name, length, and values all fit on a single line.

List format applies to everything else: non-uniform arrays, arrays of objects with different shapes, arrays containing nested structures. Each element gets a line with a hyphen prefix:

items[3]:
  - 1
  - two
  - true

When list items are objects, their fields follow on the same or subsequent lines:

results[2]:
  - id: 1
    name: First
  - id: 2
    name: Second
    extra: true

Between these three forms, TOON covers the full range of JSON arrays. The encoder picks the most compact form automatically based on what it finds in the data.

Objects and Nesting: Borrowed from YAML

For objects, TOON uses YAML-style indentation: key-value pairs, one per line, with nesting expressed through increased indent levels rather than braces:

user:
  id: 123
  name: Ada
  address:
    city: London
    country: UK

If you’ve worked with YAML, this is immediately readable. The difference is that TOON doesn’t inherit YAML’s extensive quoting rules, multi-document syntax, anchors, aliases, or the long list of edge cases that make YAML parsers complicated. TOON takes only the indentation-based structure and leaves everything else behind.

This combination of YAML-style nesting for objects and CSV-style tabular layout for uniform arrays is what makes TOON work for mixed structures. Real data usually has both: a top-level object with metadata keys, nested objects describing context, and then one or more large arrays of records. TOON handles each part with the right representation:

context:
  task: Our favorite hikes together
  location: Boulder
  season: spring_2025
friends[3]: ana,luis,sam
hikes[3]{id,name,distanceKm,elevationGain,companion,wasSunny}:
  1,Blue Lake Trail,7.5,320,ana,true
  2,Ridge Overlook,9.2,540,luis,false
  3,Wildflower Loop,5.1,180,sam,true

That’s a complete document with nested objects, a primitive array, and a tabular array, all in 8 lines. The equivalent JSON runs to about 235 tokens. This TOON representation uses around 106.

How TOON Compares to the Other Formats

Understanding TOON is easier when you place it next to the formats it’s designed to replace or complement. Each has a different trade-off profile.

JSON

JSON is the baseline. It’s universal, every language has a parser, and any LLM has seen enormous amounts of it. Its weakness in a prompt context is verbosity, especially for arrays of objects where field names repeat for every element.

JSON-compact (no whitespace) helps with token count but makes the output harder for both humans and models to read. At some point the structural noise and the lack of visual separation between records creates genuine parsing difficulty.

TOON uses more tokens than JSON-compact on non-tabular data. The overhead of hyphen markers in list format adds a small cost compared to [] syntax. But for tabular data, TOON is consistently more compact than any JSON variant, while remaining significantly more readable than JSON-compact.

One thing worth noting: TOON and JSON share the same data model. TOON doesn’t add types, doesn’t change semantics, doesn’t introduce concepts that JSON doesn’t have. Encoding and decoding are lossless and deterministic. If you have a JSON pipeline, TOON fits in as a serialization format: you convert to TOON for the prompt, decode back to JSON on the way out.

YAML

YAML is TOON’s closest relative in terms of syntax. Both use indentation for nesting. Both are more readable than JSON. YAML came first and has widespread adoption in configuration management.

For objects and non-uniform data, YAML and TOON are comparable in compactness. Where TOON diverges is with arrays of objects. YAML’s list syntax for objects looks like this:

users:
  - id: 1
    name: Alice
    role: admin
  - id: 2
    name: Bob
    role: user

That’s two records, with id, name, and role repeated for each. In TOON:

users[2]{id,name,role}:
  1,Alice,admin
  2,Bob,user

The larger the dataset, the bigger the difference. On the benchmark datasets, TOON used about 26% fewer tokens than YAML across mixed-structure data.

YAML also has well-known complexity: the string true is a boolean, no is a boolean in some parsers, 1.0 might or might not be a float depending on the version, and there’s a long list of special cases that bite you when you least expect them. TOON’s type rules are simpler and more predictable: unquoted true, false, and null are typed literals; anything else that looks like a number is a number; everything else is a string. The quoting rules are explicit and minimal.

CSV

CSV is the most compact format for flat tabular data. A CSV file with a header row and a thousand records is smaller than equivalent TOON, because TOON’s array length declaration ([N]) adds a few characters per array.

But CSV has a hard limitation: it can only represent flat tables. The moment you have nested objects, multiple related arrays, or anything that isn’t a rectangle, CSV either can’t represent it at all or forces you to flatten it in ways that lose information. A TOON document with nested context and multiple arrays can’t be expressed as a single CSV file.

For pure tabular data going to an LLM, CSV is actually a reasonable choice and the token difference is small. TOON adds maybe 5-10% overhead in exchange for explicit array lengths and field headers, which help with structural validation and LLM reliability. For anything more complex than a flat table, CSV isn’t a contender.

XML

XML is the most verbose format in the comparison. It wraps every value in opening and closing tags, and even minimal XML has significant structural overhead. The benchmark data puts XML consistently at 1.5-2x the token count of JSON, and 3-5x the count of TOON for tabular data.

XML has strengths that TOON doesn’t need to compete with: namespaces, schemas, XPath queries, decades of tooling for document-centric data. If you’re working with SOAP APIs or SVG or document markup, XML is what it is. For LLM prompts where you control the format, it’s hard to make a case for XML.

Quoting Rules: Minimal by Design

One of the quieter but important design decisions in TOON is that quoting is the exception, not the rule. Strings are unquoted unless they have to be quoted.

A string must be quoted if it’s empty, if it starts or ends with whitespace, if it’s identical to true, false, or null, if it looks like a number (including things like 05 with a leading zero), or if it contains structural characters like colons, brackets, or the active delimiter.

Everything else can be unquoted. Strings with internal spaces, Unicode characters, and emoji are all fine without quotes:

message: Hello 世界 👋
note: This value has internal spaces
city: New York

This matters because unquoted strings are shorter. In a dense tabular dataset where most values are clean strings or numbers, the quoting overhead is minimal. Quotes only appear where they’re actually needed to resolve ambiguity.

The escape sequences are also deliberately limited: \\, \", \n, \r, \t are valid. Nothing else. No \u escapes, no \x codes. This isn’t oversight; it’s intentional simplicity. Fewer valid escapes means fewer ways to encode the same string differently, which makes validation stricter and parsing more predictable.

Key Folding: Going Deeper on Nested Objects

For deeply nested objects that follow a chain structure (one key leading to one key leading to one key, all the way down), TOON has an optional encoding feature called key folding.

Without folding:

data:
  metadata:
    items[2]: a,b

With key folding enabled:

data.metadata.items[2]: a,b

Three nesting levels collapse into a single dotted-path key. For deeply nested configuration data this can meaningfully reduce the line count. The fold only applies to chains where each level has exactly one key; the moment a level has multiple keys, the chain breaks and folding stops.

Folded keys can be expanded back into nested objects at decode time with the matching expandPaths option, making the round-trip lossless.

Delimiter Options

By default, TOON uses commas to separate values in arrays. But comma is a common character in human-readable values: addresses, names with commas, descriptions. When your data contains commas, you’d need to quote values that contain them, and quoting adds tokens.

TOON supports two alternatives: tabs and pipes. The delimiter is declared in the array header:

items[2|]{sku|name|qty|price}:
  A1|Widget|2|9.99
  B2|Gadget|1|14.5

Or with tab as delimiter (where \t represents an actual tab character):

items[2	]{sku	name	qty	price}:
  A1	Widget	2	9.99
  B2	Gadget	1	14.5

Tab delimiter has an interesting property: tabs tokenize efficiently with most tokenizers, often as a single token. For token-heavy tabular datasets, switching from comma to tab delimiter can reduce token count by another few percent. Not dramatic, but it’s available when you need to squeeze out every token.

The [N] Length Marker: More Than Just a Count

The array length declaration in every TOON array header ([3], [100], [0]) might look like a minor detail. It’s actually one of the more useful properties when working with LLM output.

When you ask an LLM to generate TOON, the model declares upfront how many rows it will produce. Then it produces the rows. If the model gets truncated by a context limit, or if something goes wrong mid-generation, the declared count won’t match the actual row count. That mismatch is detectable: it’s a validation failure, not a silent data corruption.

TOON’s strict mode decoder checks this automatically. If the declared length doesn’t match the actual number of rows, it throws an error. Same for field count mismatches in a tabular array: if row 47 has five values but the header declared four fields, the decoder catches it immediately.

This makes TOON-encoded LLM output significantly more auditable than JSON or YAML alternatives. With JSON, a truncated array is still valid JSON; you just silently have fewer elements than you expected. With TOON, the count is in the document itself.

When Not to Use TOON

TOON is not the right choice in every situation. It’s worth being direct about the cases where you should reach for something else.

Deeply non-uniform structures (data where every object has different keys, or where objects are highly irregular) don’t benefit from TOON’s tabular representation. If none of your arrays qualify for tabular format, TOON’s overhead adds tokens rather than saving them, and you’d be better off with JSON-compact.

Pure flat tables: if your data is a single, flat CSV-style table with no nesting, no context objects, no secondary arrays, CSV is smaller. TOON’s per-array overhead (the length declaration and field header) only pays off when the table has enough rows to amortize the header cost, and when the complexity of the data warrants the added structure.

Latency-critical applications: token count and inference latency are related but not the same. Some deployments, especially local or quantized models, process certain formats faster independent of token count. If you’re optimizing for time-to-first-token, measure both formats on your actual setup before committing.

Existing JSON pipelines: if your entire stack already handles JSON fluently, the benefit of TOON needs to outweigh the integration cost. For batch processing workflows where you’re piping data to an LLM and getting structured output back, the conversion and validation overhead needs a concrete payoff. It usually exists for large, uniform datasets. For small or irregular payloads, the overhead might not be worth it.

Using TOON at toon.micru.org

The Micru TOON tool lets you paste in JSON and get TOON output immediately, or go the other direction: paste TOON and decode it back to JSON. You can also validate TOON documents and check for strict mode errors.

This is useful for a few workflows: inspecting what TOON output looks like before committing to the format, verifying that a document round-trips correctly, and debugging TOON generated by an LLM. The validation feedback in particular is helpful when you’re tuning a prompt to reliably produce TOON output and something in the generated document doesn’t parse.

The Broader Context

TOON fits into a small but growing space of formats that are reconsidering data serialization from the perspective of LLM efficiency rather than machine-to-machine communication. The assumption that JSON is the universal format for structured data made sense when the consumer was always another program. When the consumer is sometimes a language model operating under token constraints, that assumption is worth revisiting.

What makes TOON’s approach interesting is that it doesn’t abandon the JSON data model. It’s not a new type system, not a schema language, not a configuration format with its own semantics. It’s a more efficient way to write the same data that JSON already represents. That makes it relatively easy to adopt: convert at the edge, decode on the way out, use all the same data structures you already have.

The format is also genuinely new, still in active development, still accumulating real-world feedback on where it works and where it doesn’t. The [N] count in the header, the tabular encoding for uniform arrays, and the explicit delimiter scoping are specific bets about what helps LLMs and what can be validated automatically. The benchmarks suggest those bets are mostly right, but this is still early.

Where to Go From Here

If you want to try TOON, toon.micru.org is the fastest path. Paste in some JSON and see the output.

If you want to use it programmatically, the TypeScript library is @toon-format/toon: encode(data) and decode(toon) cover the basics, with options for key folding, delimiter selection, and strict mode validation.

If you want to understand the complete syntax rules, the TOON specification lives at github.com/toon-format/spec. It’s thorough and readable, covering everything from the ABNF grammar for array headers to the normalization rules for non-JSON types like Date and BigInt.

And if you came here from the Data Formats tools announcement, the other five tools (CSV, JSON, TOML, XML, and YAML) are all at their respective subdomains under micru.org.