Compact, human-readable serialization format for LLM contexts with 30-60% token reduction vs JSON. Combines YAML-like indentation with CSV-like tabular arrays. Working towards full compatibility with the official TOON specification.
Key Features: Minimal syntax • TOON Encoding and Decoding • Tabular arrays for uniform data • Array length validation • Java 17 • full Jackson Annotation Support • Null-safe by default (NullAway + JSpecify) • Comprehensive test coverage.
JToon is available on Maven Central. Add it to your project using your preferred build tool:
Gradle (Groovy DSL):
dependencies {
implementation 'dev.toonformat:jtoon:2.0.2'
}Gradle (Kotlin DSL):
dependencies {
implementation("dev.toonformat:jtoon:2.0.2")
}Maven:
<dependency>
<groupId>dev.toonformat</groupId>
<artifactId>jtoon</artifactId>
<version>2.0.2</version>
</dependency>Note: See the latest version on Maven Central (also shown in the badge above).
You can also download the JAR directly from the GitHub Releases page and add it to your project's classpath.
importdev.toonformat.jtoon.JToon;
importjava.util.*;
recordUser(intid, Stringname, List<String> tags, booleanactive, List<?> preferences) {}
recordData(Useruser) {}
Useruser = newUser(123, "Ada", List.of("reading", "gaming"), true, List.of());
Datadata = newData(user);
System.out.println(JToon.encode(data));Output:
user:
id: 123name: Adatags[2]: reading,gamingactive: truepreferences[0]:Some Java-specific types are automatically normalized for LLM-safe output:
| Input Type | Output |
|---|---|
| Number (finite) | Decimal form; -0 → 0; whole numbers as integers |
Number (NaN, ±Infinity) | null |
BigInteger | Integer if within Long range, otherwise string (no quotes) |
BigDecimal | Decimal number |
LocalDateTime | ISO date-time string in quotes |
LocalDate | ISO date string in quotes |
LocalTime | ISO time string in quotes |
ZonedDateTime | ISO offset date-time string in quotes |
OffsetDateTime | ISO offset date-time string in quotes |
Instant | ISO instant string in quotes |
java.util.Date | ISO instant string in quotes |
Optional<T> | Unwrapped value or null if empty |
Stream<T> | Materialized to array |
Map | Object with string keys |
Collection, arrays | Arrays |
Converts any Java object or JSON-string to TOON format.
Parameters:
value– Any Java object (Map, List, primitive, or nested structure). Non-serializable values are converted tonull. Java temporal types are converted to ISO strings, Optional is unwrapped, and Stream is materialized.options– Optional encoding options (EncodeOptionsrecord):indent– Number of spaces per indentation level (default:2)delimiter– Delimiter enum for array values and tabular rows:Delimiter.COMMA(default),Delimiter.TAB, orDelimiter.PIPElengthMarker– Boolean to prefix array lengths with#(default:false)flatten– Boolean to key folding to collapse single-key wrapper chains (default:OFF).flattenDepth– maximum number of segments to fold (default:Infinity)
For encodeJson overloads:
json– A valid JSON string to be parsed and encoded. Invalid or blank JSON throwsIllegalArgumentException.
Returns:
A TOON-formatted string with no trailing newline or spaces.
Example:
importdev.toonformat.jtoon.JToon;
importcom.fasterxml.jackson.annotation.JsonIgnore;
importjava.util.*;
recordItem(Stringsku, intqty, doubleprice, @JsonIgnoredoubleinternPrice) {}
recordData(List<Item> items) {}
Itemitem1 = newItem("A1", 2, 9.99, 8.50);
Itemitem2 = newItem("B2", 1, 14.5, 14.0);
Datadata = newData(List.of(item1, item2));
System.out.println(JToon.encode(data));Output:
items[2]{sku,qty,price}:
A1,2,9.99B2,1,14.5The Jackson Annotation @JsonIgnore will help to keep fields from exposing.
Stringjson = """{ "user": { "id": 123, "name": "Ada", "tags": ["reading", "gaming"] }}""";
System.out.println(JToon.encodeJson(json));Output:
user:
id: 123name: Adatags[2]: reading,gamingThe delimiter option allows you to choose between comma (default), tab, or pipe delimiters for array values and tabular rows. Alternative delimiters can provide additional token savings in specific contexts.
Using tab delimiters instead of commas can reduce token count further, especially for tabular data:
importdev.toonformat.jtoon.*;
importjava.util.*;
recordItem(Stringsku, Stringname, intqty, doubleprice) {}
recordData(List<Item> items) {}
Itemitem1 = newItem("A1", "Widget", 2, 9.99);
Itemitem2 = newItem("B2", "Gadget", 1, 14.5);
Datadata = newData(List.of(item1, item2));
EncodeOptionsoptions = newEncodeOptions(2, Delimiter.TAB, false, KeyFolding.OFF, 3);
System.out.println(JToon.encode(data, options));Output:
items[2 ]{sku name qty price}:
A1 Widget 2 9.99B2 Gadget 1 14.5Benefits:
- Tabs are single characters and often tokenize more efficiently than commas.
- Tabs rarely appear in natural text, reducing the need for quote-escaping.
- The delimiter is explicitly encoded in the array header, making it self-descriptive.
Considerations:
- Some terminals and editors may collapse or expand tabs visually.
- String values containing tabs will still require quoting.
Pipe delimiters offer a middle ground between commas and tabs:
// Using the same Item and Data records from aboveEncodeOptionsoptions = newEncodeOptions(2, Delimiter.PIPE, false, KeyFolding.OFF, 3);
System.out.println(JToon.encode(data, options));Output:
items[2|]{sku|name|qty|price}:
A1|Widget|2|9.99B2|Gadget|1|14.5The lengthMarker option adds an optional hash (#) prefix to array lengths to emphasize that the bracketed value represents a count, not an index:
importdev.toonformat.jtoon.*;
importjava.util.*;
recordItem(Stringsku, intqty, doubleprice) {}
recordData(List<String> tags, List<Item> items) {}
Itemitem1 = newItem("A1", 2, 9.99);
Itemitem2 = newItem("B2", 1, 14.5);
Datadata = newData(List.of("reading", "gaming", "coding"), List.of(item1, item2));
System.out.println(JToon.encode(data, newEncodeOptions(2, Delimiter.COMMA, true, KeyFolding.OFF, 3)));
// tags[#3]: reading,gaming,coding// items[#2]{sku,qty,price}:// A1,2,9.99// B2,1,14.5// Works with custom delimitersSystem.out.println(JToon.encode(data, newEncodeOptions(2, Delimiter.PIPE, true, KeyFolding.OFF, 3)));
// tags[#3|]: reading|gaming|coding// items[#2|]{sku|qty|price}:// A1|2|9.99// B2|1|14.5Converts TOON-formatted strings back to Java objects or JSON.
Parameters:
toon– TOON-formatted input stringoptions– Optional decoding options (DecodeOptionsrecord):indent– Number of spaces per indentation level (default:2)delimiter– Expected delimiter:Delimiter.COMMA(default),Delimiter.TAB, orDelimiter.PIPEstrict– Boolean for validation mode. Whentrue(default), throwsIllegalArgumentExceptionon invalid input. Whenfalse, returnsnullon errors.expandPaths– Boolean Path expansion mode for dotted keys (default:OFF).
Returns:
For decode: A Java object (Map for objects, List for arrays, primitives for scalars, or null)
For decodeToJson: A JSON string representation
Example:
importdev.toonformat.jtoon.JToon;
Stringtoon = """ users[2]{id,name,role}: 1,Alice,admin 2,Bob,user """;
// Decode to Java objectsObjectresult = JToon.decode(toon);
// Decode directly to JSON stringStringjson = JToon.decodeToJson(toon);importdev.toonformat.jtoon.*;
importjava.util.*;
// Original dataMap<String, Object> data = newLinkedHashMap<>();
data.put("id", 123);
data.put("name", "Ada");
data.put("tags", Arrays.asList("dev", "admin"));
// Encode to TOONStringtoon = JToon.encode(data);
// Decode back to objectsObjectdecoded = JToon.decode(toon);
// Values are preserved (note: integers decode as Long)importdev.toonformat.jtoon.*;
Stringtoon = "tags[3|]: a|b|c";
// Decode with pipe delimiterDecodeOptionsoptions = newDecodeOptions(2, Delimiter.PIPE, true);
Objectresult = JToon.decode(toon, options);
// Lenient mode (returns null on errors instead of throwing)DecodeOptionslenient = DecodeOptions.withStrict(false);
Objectresult2 = JToon.decode(invalidToon, lenient);CI/CD: GitHub Actions • Java 17 • Coverage enforcement • PR coverage comments
# Build (includes tests, checks, coverage)
./gradlew build
# Run tests only
./gradlew test# Update dependency verification metadata (required when adding/updating dependencies)
./gradlew --write-verification-metadata sha256 build cyclonedxBom -x testSee CONTRIBUTING.md for full development guidelines.
This project is 100% compliant with TOON specification. Release conformance enforced on CI/CD.
- 📘 Full Documentation - Extended guides and references
- 🔧 API Reference - Detailed Javadoc
- 📋 Format Specification - TOON syntax and rules
- 📜 TOON Spec - Official specification
- 🐛 Issues - Bug reports and features
- 🤝 Contributing - Contribution guidelines
MIT License – see LICENSE for details