Repository files navigation

JToon – TOON Format for Java

BuildReleaseMaven CentralCoverageSPEC v4.1.1License: MIT

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.

Installation

Maven Central

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).

Alternative: Manual Installation

You can also download the JAR directly from the GitHub Releases page and add it to your project's classpath.

Quick Start

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]:

Type Conversions

Some Java-specific types are automatically normalized for LLM-safe output:

Input TypeOutput
Number (finite)Decimal form; -00; whole numbers as integers
Number (NaN, ±Infinity)null
BigIntegerInteger if within Long range, otherwise string (no quotes)
BigDecimalDecimal number
LocalDateTimeISO date-time string in quotes
LocalDateISO date string in quotes
LocalTimeISO time string in quotes
ZonedDateTimeISO offset date-time string in quotes
OffsetDateTimeISO offset date-time string in quotes
InstantISO instant string in quotes
java.util.DateISO instant string in quotes
Optional<T>Unwrapped value or null if empty
Stream<T>Materialized to array
MapObject with string keys
Collection, arraysArrays

API

JToon.encode(Object value): String

JToon.encode(Object value, EncodeOptions options): String

JToon.encodeJson(String json): String

JToon.encodeJson(String json, EncodeOptions options): String

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 to null. Java temporal types are converted to ISO strings, Optional is unwrapped, and Stream is materialized.
  • options – Optional encoding options (EncodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Delimiter enum for array values and tabular rows: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • lengthMarker – 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 throws IllegalArgumentException.

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.5

The Jackson Annotation @JsonIgnore will help to keep fields from exposing.

Encode a plain JSON string

Stringjson = """{ "user": { "id": 123, "name": "Ada", "tags": ["reading", "gaming"] }}""";
System.out.println(JToon.encodeJson(json));

Output:

user:
id: 123name: Adatags[2]: reading,gaming

Delimiter Options

The 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.

Tab Delimiter (\t)

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.5

Benefits:

  • 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 Delimiter (|)

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.5

Length Marker Option

The 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.5

JToon.decode(String toon): Object

JToon.decode(String toon, DecodeOptions options): Object

JToon.decodeToJson(String toon): String

JToon.decodeToJson(String toon, DecodeOptions options): String

Converts TOON-formatted strings back to Java objects or JSON.

Parameters:

  • toon – TOON-formatted input string
  • options – Optional decoding options (DecodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Expected delimiter: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • strict – Boolean for validation mode. When true (default), throws IllegalArgumentException on invalid input. When false, returns null on 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);

Round-Trip Conversion

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)

Custom Decode Options

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

Development

# 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 test

See CONTRIBUTING.md for full development guidelines.

Project Status

This project is 100% compliant with TOON specification. Release conformance enforced on CI/CD.

Documentation

License

MIT License – see LICENSE for details

About

☕ Community-driven Java implementation of TOON

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

154 stars

Watchers

4 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

JToon – TOON Format for Java

BuildReleaseMaven CentralCoverageSPEC v4.1.1License: MIT

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.

Installation

Maven Central

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).

Alternative: Manual Installation

You can also download the JAR directly from the GitHub Releases page and add it to your project's classpath.

Quick Start

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]:

Type Conversions

Some Java-specific types are automatically normalized for LLM-safe output:

Input TypeOutput
Number (finite)Decimal form; -00; whole numbers as integers
Number (NaN, ±Infinity)null
BigIntegerInteger if within Long range, otherwise string (no quotes)
BigDecimalDecimal number
LocalDateTimeISO date-time string in quotes
LocalDateISO date string in quotes
LocalTimeISO time string in quotes
ZonedDateTimeISO offset date-time string in quotes
OffsetDateTimeISO offset date-time string in quotes
InstantISO instant string in quotes
java.util.DateISO instant string in quotes
Optional<T>Unwrapped value or null if empty
Stream<T>Materialized to array
MapObject with string keys
Collection, arraysArrays

API

JToon.encode(Object value): String

JToon.encode(Object value, EncodeOptions options): String

JToon.encodeJson(String json): String

JToon.encodeJson(String json, EncodeOptions options): String

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 to null. Java temporal types are converted to ISO strings, Optional is unwrapped, and Stream is materialized.
  • options – Optional encoding options (EncodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Delimiter enum for array values and tabular rows: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • lengthMarker – 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 throws IllegalArgumentException.

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.5

The Jackson Annotation @JsonIgnore will help to keep fields from exposing.

Encode a plain JSON string

Stringjson = """{ "user": { "id": 123, "name": "Ada", "tags": ["reading", "gaming"] }}""";
System.out.println(JToon.encodeJson(json));

Output:

user:
id: 123name: Adatags[2]: reading,gaming

Delimiter Options

The 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.

Tab Delimiter (\t)

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.5

Benefits:

  • 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 Delimiter (|)

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.5

Length Marker Option

The 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.5

JToon.decode(String toon): Object

JToon.decode(String toon, DecodeOptions options): Object

JToon.decodeToJson(String toon): String

JToon.decodeToJson(String toon, DecodeOptions options): String

Converts TOON-formatted strings back to Java objects or JSON.

Parameters:

  • toon – TOON-formatted input string
  • options – Optional decoding options (DecodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Expected delimiter: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • strict – Boolean for validation mode. When true (default), throws IllegalArgumentException on invalid input. When false, returns null on 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);

Round-Trip Conversion

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)

Custom Decode Options

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

Development

# 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 test

See CONTRIBUTING.md for full development guidelines.

Project Status

This project is 100% compliant with TOON specification. Release conformance enforced on CI/CD.

Documentation

License

MIT License – see LICENSE for details

About

☕ Community-driven Java implementation of TOON

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

154 stars

Watchers

4 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

JToon – TOON Format for Java

BuildReleaseMaven CentralCoverageSPEC v4.1.1License: MIT

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.

Installation

Maven Central

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).

Alternative: Manual Installation

You can also download the JAR directly from the GitHub Releases page and add it to your project's classpath.

Quick Start

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]:

Type Conversions

Some Java-specific types are automatically normalized for LLM-safe output:

Input TypeOutput
Number (finite)Decimal form; -00; whole numbers as integers
Number (NaN, ±Infinity)null
BigIntegerInteger if within Long range, otherwise string (no quotes)
BigDecimalDecimal number
LocalDateTimeISO date-time string in quotes
LocalDateISO date string in quotes
LocalTimeISO time string in quotes
ZonedDateTimeISO offset date-time string in quotes
OffsetDateTimeISO offset date-time string in quotes
InstantISO instant string in quotes
java.util.DateISO instant string in quotes
Optional<T>Unwrapped value or null if empty
Stream<T>Materialized to array
MapObject with string keys
Collection, arraysArrays

API

JToon.encode(Object value): String

JToon.encode(Object value, EncodeOptions options): String

JToon.encodeJson(String json): String

JToon.encodeJson(String json, EncodeOptions options): String

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 to null. Java temporal types are converted to ISO strings, Optional is unwrapped, and Stream is materialized.
  • options – Optional encoding options (EncodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Delimiter enum for array values and tabular rows: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • lengthMarker – 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 throws IllegalArgumentException.

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.5

The Jackson Annotation @JsonIgnore will help to keep fields from exposing.

Encode a plain JSON string

Stringjson = """{ "user": { "id": 123, "name": "Ada", "tags": ["reading", "gaming"] }}""";
System.out.println(JToon.encodeJson(json));

Output:

user:
id: 123name: Adatags[2]: reading,gaming

Delimiter Options

The 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.

Tab Delimiter (\t)

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.5

Benefits:

  • 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 Delimiter (|)

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.5

Length Marker Option

The 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.5

JToon.decode(String toon): Object

JToon.decode(String toon, DecodeOptions options): Object

JToon.decodeToJson(String toon): String

JToon.decodeToJson(String toon, DecodeOptions options): String

Converts TOON-formatted strings back to Java objects or JSON.

Parameters:

  • toon – TOON-formatted input string
  • options – Optional decoding options (DecodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Expected delimiter: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • strict – Boolean for validation mode. When true (default), throws IllegalArgumentException on invalid input. When false, returns null on 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);

Round-Trip Conversion

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)

Custom Decode Options

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

Development

# 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 test

See CONTRIBUTING.md for full development guidelines.

Project Status

This project is 100% compliant with TOON specification. Release conformance enforced on CI/CD.

Documentation

License

MIT License – see LICENSE for details

About

☕ Community-driven Java implementation of TOON

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

154 stars

Watchers

4 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

JToon – TOON Format for Java

BuildReleaseMaven CentralCoverageSPEC v4.1.1License: MIT

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.

Installation

Maven Central

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).

Alternative: Manual Installation

You can also download the JAR directly from the GitHub Releases page and add it to your project's classpath.

Quick Start

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]:

Type Conversions

Some Java-specific types are automatically normalized for LLM-safe output:

Input TypeOutput
Number (finite)Decimal form; -00; whole numbers as integers
Number (NaN, ±Infinity)null
BigIntegerInteger if within Long range, otherwise string (no quotes)
BigDecimalDecimal number
LocalDateTimeISO date-time string in quotes
LocalDateISO date string in quotes
LocalTimeISO time string in quotes
ZonedDateTimeISO offset date-time string in quotes
OffsetDateTimeISO offset date-time string in quotes
InstantISO instant string in quotes
java.util.DateISO instant string in quotes
Optional<T>Unwrapped value or null if empty
Stream<T>Materialized to array
MapObject with string keys
Collection, arraysArrays

API

JToon.encode(Object value): String

JToon.encode(Object value, EncodeOptions options): String

JToon.encodeJson(String json): String

JToon.encodeJson(String json, EncodeOptions options): String

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 to null. Java temporal types are converted to ISO strings, Optional is unwrapped, and Stream is materialized.
  • options – Optional encoding options (EncodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Delimiter enum for array values and tabular rows: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • lengthMarker – 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 throws IllegalArgumentException.

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.5

The Jackson Annotation @JsonIgnore will help to keep fields from exposing.

Encode a plain JSON string

Stringjson = """{ "user": { "id": 123, "name": "Ada", "tags": ["reading", "gaming"] }}""";
System.out.println(JToon.encodeJson(json));

Output:

user:
id: 123name: Adatags[2]: reading,gaming

Delimiter Options

The 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.

Tab Delimiter (\t)

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.5

Benefits:

  • 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 Delimiter (|)

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.5

Length Marker Option

The 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.5

JToon.decode(String toon): Object

JToon.decode(String toon, DecodeOptions options): Object

JToon.decodeToJson(String toon): String

JToon.decodeToJson(String toon, DecodeOptions options): String

Converts TOON-formatted strings back to Java objects or JSON.

Parameters:

  • toon – TOON-formatted input string
  • options – Optional decoding options (DecodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Expected delimiter: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • strict – Boolean for validation mode. When true (default), throws IllegalArgumentException on invalid input. When false, returns null on 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);

Round-Trip Conversion

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)

Custom Decode Options

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

Development

# 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 test

See CONTRIBUTING.md for full development guidelines.

Project Status

This project is 100% compliant with TOON specification. Release conformance enforced on CI/CD.

Documentation

License

MIT License – see LICENSE for details

About

☕ Community-driven Java implementation of TOON

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

154 stars

Watchers

4 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

JToon – TOON Format for Java

BuildReleaseMaven CentralCoverageSPEC v4.1.1License: MIT

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.

Installation

Maven Central

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).

Alternative: Manual Installation

You can also download the JAR directly from the GitHub Releases page and add it to your project's classpath.

Quick Start

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]:

Type Conversions

Some Java-specific types are automatically normalized for LLM-safe output:

Input TypeOutput
Number (finite)Decimal form; -00; whole numbers as integers
Number (NaN, ±Infinity)null
BigIntegerInteger if within Long range, otherwise string (no quotes)
BigDecimalDecimal number
LocalDateTimeISO date-time string in quotes
LocalDateISO date string in quotes
LocalTimeISO time string in quotes
ZonedDateTimeISO offset date-time string in quotes
OffsetDateTimeISO offset date-time string in quotes
InstantISO instant string in quotes
java.util.DateISO instant string in quotes
Optional<T>Unwrapped value or null if empty
Stream<T>Materialized to array
MapObject with string keys
Collection, arraysArrays

API

JToon.encode(Object value): String

JToon.encode(Object value, EncodeOptions options): String

JToon.encodeJson(String json): String

JToon.encodeJson(String json, EncodeOptions options): String

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 to null. Java temporal types are converted to ISO strings, Optional is unwrapped, and Stream is materialized.
  • options – Optional encoding options (EncodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Delimiter enum for array values and tabular rows: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • lengthMarker – 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 throws IllegalArgumentException.

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.5

The Jackson Annotation @JsonIgnore will help to keep fields from exposing.

Encode a plain JSON string

Stringjson = """{ "user": { "id": 123, "name": "Ada", "tags": ["reading", "gaming"] }}""";
System.out.println(JToon.encodeJson(json));

Output:

user:
id: 123name: Adatags[2]: reading,gaming

Delimiter Options

The 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.

Tab Delimiter (\t)

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.5

Benefits:

  • 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 Delimiter (|)

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.5

Length Marker Option

The 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.5

JToon.decode(String toon): Object

JToon.decode(String toon, DecodeOptions options): Object

JToon.decodeToJson(String toon): String

JToon.decodeToJson(String toon, DecodeOptions options): String

Converts TOON-formatted strings back to Java objects or JSON.

Parameters:

  • toon – TOON-formatted input string
  • options – Optional decoding options (DecodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Expected delimiter: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • strict – Boolean for validation mode. When true (default), throws IllegalArgumentException on invalid input. When false, returns null on 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);

Round-Trip Conversion

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)

Custom Decode Options

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

Development

# 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 test

See CONTRIBUTING.md for full development guidelines.

Project Status

This project is 100% compliant with TOON specification. Release conformance enforced on CI/CD.

Documentation

License

MIT License – see LICENSE for details

About

☕ Community-driven Java implementation of TOON

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

154 stars

Watchers

4 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

JToon – TOON Format for Java

BuildReleaseMaven CentralCoverageSPEC v4.1.1License: MIT

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.

Installation

Maven Central

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).

Alternative: Manual Installation

You can also download the JAR directly from the GitHub Releases page and add it to your project's classpath.

Quick Start

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]:

Type Conversions

Some Java-specific types are automatically normalized for LLM-safe output:

Input TypeOutput
Number (finite)Decimal form; -00; whole numbers as integers
Number (NaN, ±Infinity)null
BigIntegerInteger if within Long range, otherwise string (no quotes)
BigDecimalDecimal number
LocalDateTimeISO date-time string in quotes
LocalDateISO date string in quotes
LocalTimeISO time string in quotes
ZonedDateTimeISO offset date-time string in quotes
OffsetDateTimeISO offset date-time string in quotes
InstantISO instant string in quotes
java.util.DateISO instant string in quotes
Optional<T>Unwrapped value or null if empty
Stream<T>Materialized to array
MapObject with string keys
Collection, arraysArrays

API

JToon.encode(Object value): String

JToon.encode(Object value, EncodeOptions options): String

JToon.encodeJson(String json): String

JToon.encodeJson(String json, EncodeOptions options): String

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 to null. Java temporal types are converted to ISO strings, Optional is unwrapped, and Stream is materialized.
  • options – Optional encoding options (EncodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Delimiter enum for array values and tabular rows: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • lengthMarker – 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 throws IllegalArgumentException.

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.5

The Jackson Annotation @JsonIgnore will help to keep fields from exposing.

Encode a plain JSON string

Stringjson = """{ "user": { "id": 123, "name": "Ada", "tags": ["reading", "gaming"] }}""";
System.out.println(JToon.encodeJson(json));

Output:

user:
id: 123name: Adatags[2]: reading,gaming

Delimiter Options

The 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.

Tab Delimiter (\t)

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.5

Benefits:

  • 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 Delimiter (|)

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.5

Length Marker Option

The 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.5

JToon.decode(String toon): Object

JToon.decode(String toon, DecodeOptions options): Object

JToon.decodeToJson(String toon): String

JToon.decodeToJson(String toon, DecodeOptions options): String

Converts TOON-formatted strings back to Java objects or JSON.

Parameters:

  • toon – TOON-formatted input string
  • options – Optional decoding options (DecodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Expected delimiter: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • strict – Boolean for validation mode. When true (default), throws IllegalArgumentException on invalid input. When false, returns null on 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);

Round-Trip Conversion

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)

Custom Decode Options

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

Development

# 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 test

See CONTRIBUTING.md for full development guidelines.

Project Status

This project is 100% compliant with TOON specification. Release conformance enforced on CI/CD.

Documentation

License

MIT License – see LICENSE for details

About

☕ Community-driven Java implementation of TOON

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

154 stars

Watchers

4 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

JToon – TOON Format for Java

BuildReleaseMaven CentralCoverageSPEC v4.1.1License: MIT

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.

Installation

Maven Central

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).

Alternative: Manual Installation

You can also download the JAR directly from the GitHub Releases page and add it to your project's classpath.

Quick Start

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]:

Type Conversions

Some Java-specific types are automatically normalized for LLM-safe output:

Input TypeOutput
Number (finite)Decimal form; -00; whole numbers as integers
Number (NaN, ±Infinity)null
BigIntegerInteger if within Long range, otherwise string (no quotes)
BigDecimalDecimal number
LocalDateTimeISO date-time string in quotes
LocalDateISO date string in quotes
LocalTimeISO time string in quotes
ZonedDateTimeISO offset date-time string in quotes
OffsetDateTimeISO offset date-time string in quotes
InstantISO instant string in quotes
java.util.DateISO instant string in quotes
Optional<T>Unwrapped value or null if empty
Stream<T>Materialized to array
MapObject with string keys
Collection, arraysArrays

API

JToon.encode(Object value): String

JToon.encode(Object value, EncodeOptions options): String

JToon.encodeJson(String json): String

JToon.encodeJson(String json, EncodeOptions options): String

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 to null. Java temporal types are converted to ISO strings, Optional is unwrapped, and Stream is materialized.
  • options – Optional encoding options (EncodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Delimiter enum for array values and tabular rows: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • lengthMarker – 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 throws IllegalArgumentException.

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.5

The Jackson Annotation @JsonIgnore will help to keep fields from exposing.

Encode a plain JSON string

Stringjson = """{ "user": { "id": 123, "name": "Ada", "tags": ["reading", "gaming"] }}""";
System.out.println(JToon.encodeJson(json));

Output:

user:
id: 123name: Adatags[2]: reading,gaming

Delimiter Options

The 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.

Tab Delimiter (\t)

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.5

Benefits:

  • 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 Delimiter (|)

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.5

Length Marker Option

The 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.5

JToon.decode(String toon): Object

JToon.decode(String toon, DecodeOptions options): Object

JToon.decodeToJson(String toon): String

JToon.decodeToJson(String toon, DecodeOptions options): String

Converts TOON-formatted strings back to Java objects or JSON.

Parameters:

  • toon – TOON-formatted input string
  • options – Optional decoding options (DecodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Expected delimiter: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • strict – Boolean for validation mode. When true (default), throws IllegalArgumentException on invalid input. When false, returns null on 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);

Round-Trip Conversion

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)

Custom Decode Options

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

Development

# 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 test

See CONTRIBUTING.md for full development guidelines.

Project Status

This project is 100% compliant with TOON specification. Release conformance enforced on CI/CD.

Documentation

License

MIT License – see LICENSE for details

About

☕ Community-driven Java implementation of TOON

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

154 stars

Watchers

4 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

JToon – TOON Format for Java

BuildReleaseMaven CentralCoverageSPEC v4.1.1License: MIT

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.

Installation

Maven Central

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).

Alternative: Manual Installation

You can also download the JAR directly from the GitHub Releases page and add it to your project's classpath.

Quick Start

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]:

Type Conversions

Some Java-specific types are automatically normalized for LLM-safe output:

Input TypeOutput
Number (finite)Decimal form; -00; whole numbers as integers
Number (NaN, ±Infinity)null
BigIntegerInteger if within Long range, otherwise string (no quotes)
BigDecimalDecimal number
LocalDateTimeISO date-time string in quotes
LocalDateISO date string in quotes
LocalTimeISO time string in quotes
ZonedDateTimeISO offset date-time string in quotes
OffsetDateTimeISO offset date-time string in quotes
InstantISO instant string in quotes
java.util.DateISO instant string in quotes
Optional<T>Unwrapped value or null if empty
Stream<T>Materialized to array
MapObject with string keys
Collection, arraysArrays

API

JToon.encode(Object value): String

JToon.encode(Object value, EncodeOptions options): String

JToon.encodeJson(String json): String

JToon.encodeJson(String json, EncodeOptions options): String

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 to null. Java temporal types are converted to ISO strings, Optional is unwrapped, and Stream is materialized.
  • options – Optional encoding options (EncodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Delimiter enum for array values and tabular rows: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • lengthMarker – 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 throws IllegalArgumentException.

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.5

The Jackson Annotation @JsonIgnore will help to keep fields from exposing.

Encode a plain JSON string

Stringjson = """{ "user": { "id": 123, "name": "Ada", "tags": ["reading", "gaming"] }}""";
System.out.println(JToon.encodeJson(json));

Output:

user:
id: 123name: Adatags[2]: reading,gaming

Delimiter Options

The 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.

Tab Delimiter (\t)

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.5

Benefits:

  • 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 Delimiter (|)

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.5

Length Marker Option

The 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.5

JToon.decode(String toon): Object

JToon.decode(String toon, DecodeOptions options): Object

JToon.decodeToJson(String toon): String

JToon.decodeToJson(String toon, DecodeOptions options): String

Converts TOON-formatted strings back to Java objects or JSON.

Parameters:

  • toon – TOON-formatted input string
  • options – Optional decoding options (DecodeOptions record):
    • indent – Number of spaces per indentation level (default: 2)
    • delimiter – Expected delimiter: Delimiter.COMMA (default), Delimiter.TAB, or Delimiter.PIPE
    • strict – Boolean for validation mode. When true (default), throws IllegalArgumentException on invalid input. When false, returns null on 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);

Round-Trip Conversion

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)

Custom Decode Options

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

Development

# 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 test

See CONTRIBUTING.md for full development guidelines.

Project Status

This project is 100% compliant with TOON specification. Release conformance enforced on CI/CD.

Documentation

License

MIT License – see LICENSE for details

About

☕ Community-driven Java implementation of TOON

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

154 stars

Watchers

4 watching

Forks

Releases

Packages

Contributors

Languages