java.util.json.Java21

JSON Type Definition (JTD) Validator

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.

Use Case

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.json has 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.

Features

Quick Start

import json.java21.jtd.Jtd;
import jdk.incubator.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);
}

Schema Forms

JTD defines eight mutually-exclusive schema forms:

1. Empty Schema

Accepts any JSON value:

{}

2. Ref Schema

References a definition:

{"ref": "address"}

3. Type Schema

Validates primitive types:

{"type": "string"}

Supported types: boolean, string, timestamp, int8, uint8, int16, uint16, int32, uint32, float32, float64

Integer Type Validation

Integer types (int8, uint8, int16, uint16, int32, uint32) validate based on numeric value, not textual representation:

This follows RFC 8927 §2.2.3.1: “An integer value is a number without a fractional component.”

4. Enum Schema

Validates against string values:

{"enum": ["red", "green", "blue"]}

5. Elements Schema

Validates homogeneous arrays:

{"elements": {"type": "string"}}

6. Properties Schema

Validates objects with required/optional fields:

{
  "properties": {
    "id": {"type": "string"},
    "name": {"type": "string"}
  },
  "optionalProperties": {
    "email": {"type": "string"}
  }
}

7. Values Schema

Validates objects with homogeneous values:

{"values": {"type": "string"}}

8. Discriminator Schema

Validates tagged unions:

{
  "discriminator": "type",
  "mapping": {
    "person": {"properties": {"name": {"type": "string"}}},
    "company": {"properties": {"name": {"type": "string"}}}
  }
}

Discriminator Constraints (RFC 8927 §2.2.8):

These constraints are enforced at compile-time for predictable validation behavior.

Nullable Schemas

Any schema can be made nullable by adding "nullable": true:

{"type": "string", "nullable": true}

Definitions

Schemas can define reusable components:

{
  "definitions": {
    "address": {
      "properties": {
        "street": {"type": "string"},
        "city": {"type": "string"}
      }
    }
  },
  "properties": {
    "home": {"ref": "address"},
    "work": {"ref": "address"}
  }
}

Error Reporting

Validation errors include standardized information:

[off=45 ptr=/age via=#→field:age] expected int32, got string

Building and Testing

# 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=FINE

Functional Validator API

A 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.incubator.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"

Bytecode-Generated Validators (optional, JDK 24+)

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.

Architecture

The validator uses a stack-based approach for efficient validation:

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.

RFC 8927 Compliance

This implementation is fully compliant with RFC 8927:

Performance

License

This project is part of the OpenJDK JSON API implementation and follows the same licensing terms.