A Java implementation of the JSON Type Definition (JTD) specification (RFC 8927). JTD is a schema language for JSON that provides simple, predictable validation with eight mutually-exclusive schema forms.
This interpreter-based validator is optimised for infrequent validation — loading configuration files, validating resources at startup, or one-off schema checks. Because these operations run rarely, the validated classes are unlikely to be JIT-compiled, and the interpreter's simplicity keeps startup overhead low. After validation completes, the schema and document objects are typically discarded, so the JVM is not encumbered with loaded validator classes and can focus JIT effort on the application's hot path.
For repeated hot-path validation (e.g., event processing, API gateways), consider the bytecode codegen module which generates dedicated validator classes that benefit from JIT optimisation over many invocations.
Future note:
java.util.jsonhas entered the JDK incubator (jdk.incubator.json). Once the API stabilises in the JDK itself, generated bytecode validators can depend directly on future JDK classes rather than this backport, making them even more efficient with zero library overhead.
- RFC 8927 Compliant: Full implementation of the JSON Type Definition specification
- Eight Schema Forms: Empty, Ref, Type, Enum, Elements, Properties, Values, Discriminator
- Stack-based Validation: Efficient iterative validation with comprehensive error reporting
- Immutable Design: All schema types are records, validation uses pure functions
- Rich Error Messages: Standardized error format with instance and schema paths
- Comprehensive Testing: Includes official JTD Test Suite for RFC compliance
import json.java21.jtd.Jtd;
import jdk.sandbox.java.util.json.*;
// Create a JTD schema
String schemaJson = """
{
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"age": { "type": "int32" }
},
"optionalProperties": {
"email": { "type": "string" }
}
}
""";
// Parse and validate
JsonValue schema = Json.parse(schemaJson);
JsonValue data = Json.parse("{\"id\": \"123\", \"name\": \"Alice\", \"age\": 30}");
Jtd validator = new Jtd();
Jtd.Result result = validator.validate(schema, data);
if (result.isValid()) {
System.out.println("Valid!");
} else {
result.errors().forEach(System.out::println);
}JTD defines eight mutually-exclusive schema forms:
Accepts any JSON value:
{}References a definition:
{"ref": "address"}Validates primitive types:
{"type": "string"}Supported types: boolean, string, timestamp, int8, uint8, int16, uint16, int32, uint32, float32, float64
Integer types (int8, uint8, int16, uint16, int32, uint32) validate based on numeric value, not textual representation:
- Valid integers:
3,3.0,3.000,42.00(mathematically integers) - Invalid integers:
3.1,3.14,3.0001(have fractional components)
This follows RFC 8927 §2.2.3.1: "An integer value is a number without a fractional component."
Validates against string values:
{"enum": ["red", "green", "blue"]}Validates homogeneous arrays:
{"elements": {"type": "string"}}Validates objects with required/optional fields:
{
"properties": {
"id": {"type": "string"},
"name": {"type": "string"}
},
"optionalProperties": {
"email": {"type": "string"}
}
}Validates objects with homogeneous values:
{"values": {"type": "string"}}Validates tagged unions:
{
"discriminator": "type",
"mapping": {
"person": {"properties": {"name": {"type": "string"}}},
"company": {"properties": {"name": {"type": "string"}}}
}
}Discriminator Constraints (RFC 8927 §2.2.8):
- Mapping values must be
propertiesschemas (not primitive types) - Mapped schemas cannot have
nullable: true - Mapped schemas cannot define the discriminator key in properties/optionalProperties
- The discriminator field is exempt from additionalProperties validation
These constraints are enforced at compile-time for predictable validation behavior.
Any schema can be made nullable by adding "nullable": true:
{"type": "string", "nullable": true}Schemas can define reusable components:
{
"definitions": {
"address": {
"properties": {
"street": {"type": "string"},
"city": {"type": "string"}
}
}
},
"properties": {
"home": {"ref": "address"},
"work": {"ref": "address"}
}
}Validation errors include standardized information:
[off=45 ptr=/age via=#→field:age] expected int32, got string
- off: Character offset in the JSON document
- ptr: JSON Pointer to the failing value
- via: Human-readable path to the error location
# Build the module
./mvnw compile -pl json-java21-jtd -am
# Run tests
./mvnw test -pl json-java21-jtd -am
# Run RFC compliance tests
./mvnw test -pl json-java21-jtd -am -Dtest=JtdSpecIT
# Run with detailed logging
./mvnw test -pl json-java21-jtd -am -Djava.util.logging.ConsoleHandler.level=FINEA schema can be compiled into a reusable JtdValidator -- a functional interface
(JsonValue -> JtdValidationResult) suitable for stream pipelines:
import json.java21.jtd.JtdValidator;
import json.java21.jtd.JtdValidationResult;
import jdk.sandbox.java.util.json.*;
String schemaJson = """
{ "type": "string" }
""";
JsonValue schema = Json.parse(schemaJson);
// Compile to a reusable validator (interpreter path, always available)
JtdValidator validator = JtdValidator.compileInterpreter(schema);
JtdValidationResult result = validator.validate(Json.parse("\"hello\""));
assert result.isValid();
// Use in a stream pipeline
List<JsonValue> docs = ...;
List<JsonValue> invalid = docs.stream()
.filter(doc -> !validator.validate(doc).isValid())
.toList();Errors follow RFC 8927 exactly -- each error is an (instancePath, schemaPath) pair:
JtdValidationResult result = validator.validate(Json.parse("42"));
result.errors().forEach(e ->
System.out.println(e.instancePath() + " -> " + e.schemaPath()));
// Output: "" -> "/type"For repeated hot-path validation, the json-java21-jtd-codegen module generates dedicated validator classes via the JDK 24+ ClassFile API. Use its own JtdValidator.compileGenerated(schema) factory — a separate functional interface in the json.java21.jtd.codegen package.
The validator uses a stack-based approach for efficient validation:
- Immutable Records: All schema types are immutable records
- Stack-based Validation: Iterative validation prevents stack overflow
- Lazy Resolution: References resolved only when needed
- Comprehensive Testing: Full RFC 8927 compliance test suite
See ARCHITECTURE.md for detailed implementation information. See JTD_STACK_MACHINE_SPEC.md for the interpreter specification. See JTD_CODEGEN_SPEC.md for the code generation specification.
This implementation is fully compliant with RFC 8927:
- ✅ Eight mutually-exclusive schema forms
- ✅ Standardized error format with instance and schema paths
- ✅ Primitive type validation with proper ranges
- ✅ Definition support with reference resolution
- ✅ Timestamp format validation (RFC 3339 with leap seconds)
- ✅ Discriminator tag exemption from additional properties
- Zero allocations during validation of simple types
- Stack-based validation prevents StackOverflowError
- Early exit on first validation error
- Immutable design enables safe concurrent use
- Optional codegen (JDK 24+ build) eliminates interpreter overhead for hot-path validation
This project is part of the OpenJDK JSON API implementation and follows the same licensing terms.