How to Serialize Java Objects to TOON Format Using json-io
Purpose
This post demonstrates how to serialize Java objects to TOON format using the json-io library, including nested structures, collections, and Spring AI integration.
Environment
- Java 8-24
- json-io 4.98.0
- Maven build tool
The json-io Library
json-io provides TOON serialization with a single dependency. It supports Java 8 through 24 and requires only one runtime dependency (java-util).
Add the dependency:
<dependency> <groupId>com.cedarsoftware</groupId> <artifactId>json-io</artifactId> <version>4.98.0</version></dependency>The key methods:
JsonIo.toToon(object, options)— Serialize to TOONJsonIo.fromToon(toon, options).asClass(Target.class)— Deserialize to typed objectJsonIo.fromToon(toon, options).asType(new TypeHolder<List<T>>(){})— Deserialize to generic collections
Serializing Single Objects
For a single object, TOON produces YAML-like output:
public class Person { private String name; private int age; private String department;
// constructors, getters, setters}Person person = new Person("Alice", 28, "Engineering");String toon = JsonIo.toToon(person, null);
System.out.println(toon);Output:
name: Aliceage: 28department: EngineeringI can explain the output:
- Each field is
key: valueon its own line - No braces or quotes needed
- Clean and readable
Serializing Collections (Tabular Format)
Here’s where TOON shines. Collections of uniform objects automatically use tabular format:
List<Employee> employees = Arrays.asList( new Employee("Alice Johnson", 28, "Engineering", 95000), new Employee("Bob Smith", 34, "Marketing", 78000), new Employee("Carol White", 31, "Sales", 82000));
String toon = JsonIo.toToon(employees, null);System.out.println(toon);Output:
[3]{name,age,department,salary}: Alice Johnson,28,Engineering,95000 Bob Smith,34,Marketing,78000 Carol White,31,Sales,82000The format breakdown:
[3]— Array length{name,age,department,salary}— Field header (declared once!)- Following lines — Value rows
This is where the 30-44% token savings come from.
Nested Structures
TOON handles nested structures by mixing formats appropriately:
Map<String, Object> company = new LinkedHashMap<>();company.put("name", "Acme Corp");company.put("founded", 1995);
List<Department> departments = Arrays.asList( new Department("Engineering", 50), new Department("Marketing", 20));company.put("departments", departments);
String toon = JsonIo.toToon(company, null);Output:
name: Acme Corpfounded: 1995departments: [2]{name,employeeCount}: Engineering,50 Marketing,20The outer map uses key-value pairs, while the inner array uses tabular format.
Deserializing to Typed Objects
Reading TOON back into Java objects:
String toon = """name: Aliceage: 28department: Engineering""";
Person person = JsonIo.fromToon(toon, null).asClass(Person.class);// person.getName() returns "Alice"// person.getAge() returns 28Deserializing to Generic Collections
For collections, I need to handle type erasure with TypeHolder:
String toon = """[2]{name,age}: Alice,28 Bob,34""";
List<Person> people = JsonIo.fromToon(toon, null) .asType(new TypeHolder<List<Person>>(){});
// people.size() returns 2// people.get(0).getName() returns "Alice"The TypeHolder pattern preserves generic type information at runtime.
Spring AI Integration
For Spring AI applications, there’s a dedicated starter:
<dependency> <groupId>com.cedarsoftware</groupId> <artifactId>json-io-spring-ai-toon</artifactId> <version>4.98.0</version></dependency>Use the ToonToolCallResultConverter to automatically convert tool results:
@Tool(description = "Look up employees by department", resultConverter = ToonToolCallResultConverter.class)public List<Employee> findByDepartment(String department) { return employeeRepository.findByDepartment(department);}Now when the LLM calls this tool, the result is automatically in TOON format—saving tokens with zero code changes.
Common Mistakes to Avoid
I made these mistakes when I started:
- Using prettyPrint(true) — This reverts to verbose format and eliminates token savings:
// DON'T do thisString toon = JsonIo.toToon(employees, new WriteOptions().prettyPrint(true)); // Loses savings!-
Forgetting TypeHolder for generic collections — Without it, you get raw Maps instead of typed objects.
-
Using TOON for service-to-service APIs — External services expect JSON; use
toJson()for those.
Summary
In this post, I showed how to serialize Java objects to TOON format using json-io. The key point is that JsonIo.toToon() gives you 30-44% token savings for LLM-bound data with a single method call, while JsonIo.fromToon() reads TOON back into typed Java objects.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments