JTD schema-to-bytecode compiler using the JDK 24+ ClassFile API (JEP 484). Generates Java 21-compatible .class files at runtime for ~9x faster validation on hot paths.
This codegen module is optimised for repeated hot-path validation — event processing pipelines, API gateways, message brokers, or any scenario where the same schema validates thousands or millions of documents. The generated validator classes contain only the checks the schema requires (no interpreter, no AST, no runtime stack), and after sufficient invocations the JIT compiler inlines and optimises them to near-native speed.
For infrequent validation (config loading, startup checks, one-off validation), the interpreter-based validator is simpler and avoids the codegen overhead entirely.
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.
- Build: JDK 24+ (uses
ClassFileAPI, JEP 484) - Generated bytecode: Java 21+ (classfile version 65)
- Runtime: JDK 24+ (the codegen itself runs at runtime to generate classes)
JtdCodegen.compile(schema)parses a JTD schema into an AST- The emitter walks the AST and generates bytecode using
ClassFile.of().build() - The generated class is loaded via
MethodHandles.Lookup.defineClass() - The resulting
JtdValidatorvalidates JSON in ~9x less time than the interpreter
import jdk.sandbox.java.util.json.*;
import json.java21.jtd.codegen.JtdValidator;
JsonValue schema = Json.parse("""
{"properties": {"name": {"type": "string"}, "age": {"type": "uint8"}}}
""");
JsonValue doc = Json.parse("{\"name\":\"Alice\",\"age\":30}");
// Codegen path (JDK 24+ only)
JtdValidator validator = JtdValidator.compileGenerated(schema);
var result = validator.validate(doc);
System.out.println(result.isValid()); // true
// Falls back to interpreter automatically via JtdValidator.compileInterpreter()
JtdValidator interp = json.java21.jtd.JtdValidator.compileInterpreter(schema);Benchmark results (50k warmup, 200k measured iterations):
| Schema | Valid doc | Invalid doc | Classfile size |
|---|---|---|---|
| simple-type | 6.8x faster | 2.2x | 990 B |
| enum-5 | 1.2x | 1.5x | 1,162 B |
| nullable-int | 6.3x | 2.2x | 1,205 B |
| properties-3 | 9.5x | 5.3x | 2,453 B |
| props-with-optional | 12.3x | 2.3x | 2,112 B |
| elements-of-type | 47.3x | 3.5x | 1,486 B |
| values-of-type | 17.4x | 4.0x | 1,816 B |
| nested-elements-of-props | 11.2x | 4.1x | 2,698 B |
| discriminator-2-variants | 3.3x | 3.5x | 2,671 B |
| ref-with-definitions | 1.9x | 0.7x | 3,008 B |
| deep-nesting | 0.7x | 0.5x | 3,406 B |
| worked-example-rfc8927 | 2.9x | 3.6x | 5,565 B |
| Average | 8.7x | 3.1x | — |
Codegen shines for array iteration (elements-of-type at 47x, values-of-type at 17x) and property validation (9-12x). It's slower on deeply nested schemas and ref-heavy schemas where bytecode overhead outweighs interpreter dispatch cost.
- Use codegen when: validating the same schema against many documents (e.g., event processing, API gateways)
- Use interpreter when: one-off validation, JDK 21 runtime, or schemas dominated by deep refs/nesting
# Automatically included on JDK 24+, skipped on JDK 21
./mvnw test -pl json-java21-jtd-codegen -am -Djava.util.logging.ConsoleHandler.level=INFOThe parent pom.xml uses a profile (jtd-codegen) that activates on <jdk>[24,)</jdk>, so the module is automatically skipped when building with JDK 21.