Skip to content
/ json-io Public

Convert Java to JSON/TOON and back. Supports complex object graphs, cyclic references, and TOON format for 40-50% LLM token savings

License

Notifications You must be signed in to change notification settings

jdereg/json-io

Repository files navigation

json-io infographic - JSON5, TOON, and Core Capabilities

Infographic by Guillaume Laforge

json-io is a powerful and lightweight Java library that simplifies JSON5, JSON, and TOON serialization and deserialization while handling complex object graphs with ease. Unlike basic JSON parsers, json-io preserves object references, handles polymorphic types, and maintains cyclic relationships in your data structures.

Table of Contents

Key Features

  • Full JSON5 support including single-line and multi-line comments, single-quoted strings, unquoted object keys, trailing commas, and more — while remaining fully backward compatible with standard JSON (RFC 8259)
  • TOON read/writeToken-Oriented Object Notation for LLM-optimized serialization (~40-50% fewer tokens than JSON)
  • Preserves object references and handles cyclic relationships (use cycleSupport(false) for ~35-40% faster writes on acyclic data) - for JSON/JSON5.
  • Supports polymorphic types and complex object graphs
  • Zero external dependencies (other than java-util)
  • Fully compatible with both JPMS and OSGi environments
  • Lightweight (json-io.jar is ~330K, java-util is ~700K)
  • Compatible with JDK 1.8 through JDK 24
  • The library is built with the -parameters compiler flag. Parameter names are now retained for tasks such as constructor discovery.
  • Optional unsafe mode for deserializing package-private classes, inner classes, and classes without accessible constructors (opt-in for security)
  • Annotation support — json-io's own @Io* annotations plus automatic recognition of Jackson annotations (no compile-time dependency)
  • Extensive configuration options via ReadOptionsBuilder and WriteOptionsBuilder
  • Two modes: typed Java objects (toJava()) or class-independent Maps (toMaps())
  • Parse JSON with unknown class references into a Map-of-Maps representation without requiring classes on classpath
  • Featured on json.org

Why json-io?

vs Jackson/Gson (JSON)

Capability json-io Jackson/Gson
Object graph cycles Full support (@id/@ref) None
Polymorphic types Automatic (@type when needed) Requires annotations
Configuration Zero-config default; optional annotations Annotation-heavy
Dependencies java-util (~1MB total) Multiple JARs (~2.5MB+)

Trade-off: json-io prioritizes correctness over speed. It preserves graph shape and Java type semantics—handling cycles, references, and polymorphism that break other serializers. Jackson/Gson are faster for simple DTOs, but json-io handles what they cannot.

Performance tip: Use cycleSupport(false) for ~35-40% faster writes when you know your object graph is acyclic.

vs JToon (TOON)

Capability json-io JToon
Built-in types 60+ ~15
Map key types Any serializable type Strings only
EnumSet support Yes No
Full Java serialization Yes — any object graph Limited to supported types
Cycle support ($id/$ref) Yes (opt-in) No
Annotation support @Io* + Jackson (reflective) None
Dependencies java-util only Jackson
Status Stable, production-ready Beta (v1.x.x)

json-io's TOON implementation offers comprehensive Java type coverage while JToon focuses on basic types with Jackson integration.

Annotation Support

json-io provides 25 annotations in the com.cedarsoftware.io.annotation package for controlling serialization and deserialization:

Annotation Target Purpose
@IoProperty("name") Field Rename field in JSON
@IoIgnore Field Exclude field
@IoIgnoreProperties({"a","b"}) Class Exclude fields by name
@IoAlias({"alt1","alt2"}) Field Accept alternate names on read
@IoPropertyOrder({"x","y"}) Class Control field order on write
@IoInclude(Include.NON_NULL) Field Skip null on write
@IoCreator Constructor/Method Custom deserialization constructor or static factory
@IoValue Method Single-value serialization
@IoNaming(Strategy.SNAKE_CASE) Class Naming strategy for all fields
@IoIncludeProperties({"a","b"}) Class Whitelist of included fields
@IoIgnoreType Class Exclude all fields of this type everywhere
@IoTypeInfo(LinkedList.class) Field Default concrete type when @type absent; also eliminates @type on write when runtime type matches
@IoDeserialize(as=LinkedList.class) Field/Class Force type override during deserialization; also eliminates @type on write when runtime type matches
@IoClassFactory(MyFactory.class) Class Specify a ClassFactory for deserialization
@IoGetter("fieldName") Method Custom getter method for serialization
@IoSetter("fieldName") Method Custom setter method for deserialization
@IoNonReferenceable Class Suppress @id/@ref for instances of this type
@IoNotCustomReader Class Suppress custom reader (use standard deserialization)
@IoNotCustomWritten Class Suppress custom writer (use standard serialization)
@IoCustomWriter(MyWriter.class) Class Specify custom JsonClassWriter for serialization
@IoCustomReader(MyReader.class) Class Specify custom JsonClassReader for deserialization
@IoTypeName("ShortName") Class Alias for @type in JSON (replaces FQCN)
@IoAnySetter Method Receive unrecognized JSON fields during deserialization
@IoAnyGetter Method Provide extra fields during serialization
@IoFormat("pattern") Field Per-field format pattern (String.format, DecimalFormat, DateTimeFormatter, or SimpleDateFormat)

Additionally, json-io reflectively honors Jackson annotations when they are on the classpath — with zero compile-time dependency on Jackson. Supported: @JsonProperty, @JsonIgnore, @JsonIgnoreProperties, @JsonAlias, @JsonPropertyOrder, @JsonInclude, @JsonCreator, @JsonValue, @JsonIgnoreType, @JsonTypeInfo, @JsonIncludeProperties, @JsonNaming, @JsonDeserialize, @JsonGetter, @JsonSetter, @JsonTypeName, @JsonFormat, @JsonAnySetter, @JsonAnyGetter.

Precedence: Programmatic API > json-io annotations > Jackson annotations.

See the Annotations section of the User Guide for full details and examples.

Installation

To include in your project:

Replace LATEST_VERSION with the version shown here: Maven Central

Gradle

implementation 'com.cedarsoftware:json-io:LATEST_VERSION'

Maven

<dependency>
  <groupId>com.cedarsoftware</groupId>
  <artifactId>json-io</artifactId>
  <version>LATEST_VERSION</version>
</dependency>

Spring Boot Integration

json-io provides a Spring Boot starter for seamless integration with Spring MVC and WebFlux applications.

Add the dependency:

<dependency>
  <groupId>com.cedarsoftware</groupId>
  <artifactId>json-io-spring-boot-starter</artifactId>
  <version>LATEST_VERSION</version>
</dependency>

Your REST controllers now support JSON, JSON5, and TOON formats via content negotiation:

@RestController
public class ApiController {
    @GetMapping("/data")
    public MyData getData() {
        return myData;  // Returns JSON, JSON5, or TOON based on Accept header
    }
}

Spring AI Integration

json-io provides a Spring AI module that reduces LLM token usage by ~40-50% using TOON format for tool call results and structured output parsing.

Add the dependency:

<dependency>
  <groupId>com.cedarsoftware</groupId>
  <artifactId>json-io-spring-ai-toon</artifactId>
  <version>LATEST_VERSION</version>
</dependency>

Auto-configured: tool call results are serialized to TOON automatically. For structured output, use ToonBeanOutputConverter<T>:

ToonBeanOutputConverter<Person> converter = new ToonBeanOutputConverter<>(Person.class);
Person person = chatClient.prompt()
    .user("Get info about John")
    .call()
    .entity(converter);

See the Spring Integration Guide for full details.

Quick Start

// JSON
String json = JsonIo.toJson(myObject);
MyClass obj = JsonIo.toJava(json).asClass(MyClass.class);

// TOON (~40% fewer tokens than JSON)
String toon = JsonIo.toToon(myObject, writeOptions);
MyClass obj = JsonIo.fromToon(toon, readOptions).asClass(MyClass.class);

Request TOON format for LLM applications: Accept: application/vnd.toon

Also supports WebFlux and WebClient for reactive applications.

See the Spring Integration Guide for configuration options, WebFlux usage, customizers, and Jackson coexistence modes.

TOON Format

TOON (Token-Oriented Object Notation) is an indentation-based format that produces ~40-50% fewer tokens than JSON — ideal for LLM applications where token count directly impacts cost and context window usage.

  • No braces, brackets, or commas — structure is expressed through indentation
  • No quoting for most keys and values — quotes only when the value contains special characters
  • Compact arrays — inline [N]: a,b,c or list format with - prefixed elements
  • Tabular format — arrays of uniform objects as CSV-like rows with column headers
  • Key folding — nested keys like address.city: Denver flatten one level of nesting
  • Full fidelity — most data requires no extra metadata at all. When type hints are needed for correct deserialization (e.g., polymorphic fields), you can use json-io's @Io* annotations, Jackson annotations, or explicit $type markers. For object graphs with cycles, enable cycleSupport(true) to emit $id/$ref pairs

JSON:

{"team":"Rockets","players":[{"name":"John","age":30,"position":"guard"},{"name":"Sue","age":27,"position":"forward"},{"name":"Mike","age":32,"position":"center"}]}

TOON (same data, ~45% fewer tokens):

team: Rockets
players:
  name, age, position
  John, 30, guard
  Sue, 27, forward
  Mike, 32, center

When an array contains uniform objects, TOON automatically uses tabular format — a compact CSV-like layout with column headers. Mixed or nested objects use indented format instead.

Write / Read:

// Write TOON
String toon = JsonIo.toToon(myObject);

// Read TOON back to typed Java object
Person p = JsonIo.fromToon(toon).asClass(Person.class);

// Read TOON to Maps (no class needed)
Map map = JsonIo.fromToon(toon).asMap();

Supported Types (60+ built-in)

json-io handles your business objects, DTOs, and Records automatically—no annotations required. It also provides optimized handling for these built-in types:

Category Types
Primitives byte, short, int, long, float, double, boolean, char + wrappers
Numbers BigInteger, BigDecimal, AtomicInteger, AtomicLong, AtomicBoolean
Date/Time Date, Calendar, Instant, LocalDate, LocalTime, LocalDateTime, ZonedDateTime, OffsetDateTime, OffsetTime, Duration, Period, Year, YearMonth, MonthDay, TimeZone, ZoneId, ZoneOffset, java.sql.Date, Timestamp
Strings String, StringBuffer, StringBuilder, char[], CharBuffer
Binary byte[], ByteBuffer, BitSet
IDs UUID, URI, URL, Class, Locale, Currency, Pattern, File, Path
Geometric Color, Dimension, Point, Rectangle, Insets
Other Enum (any), Throwable, all Collection, Map, EnumSet, and array types

See the complete type comparison showing json-io's comprehensive support vs other TOON implementations.

Documentation

Release Maven Central

Bundling JPMS & OSGi
Java JDK 1.8+ (multi-release JAR with module-info.class)
Package com.cedarsoftware.io

API — Static methods on JsonIo: toJson(), toJava(), toMaps(), toToon(), fromToon(), formatJson(), deepCopy()

Configure via ReadOptionsBuilder and WriteOptionsBuilder. Use ClassFactory for difficult-to-instantiate classes.

Logging

json-io uses java.util.logging to minimize dependencies. See the user guide to route logs to SLF4J or Log4j 2.


For useful Java utilities, check out java-util

About

Convert Java to JSON/TOON and back. Supports complex object graphs, cyclic references, and TOON format for 40-50% LLM token savings

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors