Skip to main content

juneau-bean-jsonschema

The juneau-bean-jsonschema module provides Java beans for working with JSON Schema documents following the JSON Schema Draft 2020-12 specification.

Overview

This module contains predefined POJOs for representing and manipulating JSON Schema documents programmatically. These beans can be serialized to any format supported by Juneau (JSON, XML, HTML, etc.), making it easy to generate and consume JSON Schema documents in your applications.

Key Features

  • Full Draft 2020-12 Support: All properties and validation keywords from the latest specification
  • Backward Compatibility: Deprecated Draft 04 properties are still supported for legacy schemas
  • Fluent API: Method chaining for intuitive schema construction
  • Type Safety: Uses enums and typed collections
  • Format Agnostic: Serialize to JSON, XML, HTML, or any other Juneau-supported format

Basic Usage

Creating a Simple Schema

import org.apache.juneau.bean.jsonschema.*;
import org.apache.juneau.json.*;

// Create a schema for a person object
JsonSchema schema = new JsonSchema()
.setIdUri("https://example.com/person.schema.json")
.setSchemaVersionUri("https://json-schema.org/draft/2020-12/schema")
.setTitle("Person")
.setDescription("A person object")
.setType(JsonType.OBJECT)
.addProperties(
new JsonSchemaProperty("firstName", JsonType.STRING)
.setMinLength(1)
.setMaxLength(50),
new JsonSchemaProperty("lastName", JsonType.STRING)
.setMinLength(1)
.setMaxLength(50),
new JsonSchemaProperty("age", JsonType.INTEGER)
.setMinimum(0)
.setExclusiveMaximum(150)
)
.addRequired("firstName", "lastName");

// Serialize to JSON
String json = JsonSerializer.DEFAULT_SORTED.serialize(schema);

Output:

{
"$id": "https://example.com/person.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "A person object",
"properties": {
"age": {
"exclusiveMaximum": 150,
"minimum": 0,
"type": "integer"
},
"firstName": {
"maxLength": 50,
"minLength": 1,
"type": "string"
},
"lastName": {
"maxLength": 50,
"minLength": 1,
"type": "string"
}
},
"required": ["firstName", "lastName"],
"title": "Person",
"type": "object"
}

Common Schema Patterns

String Constraints

// String with pattern and length constraints
JsonSchema schema = new JsonSchema()
.setType(JsonType.STRING)
.setMinLength(5)
.setMaxLength(100)
.setPattern("^[A-Za-z0-9]+$")
.addExamples("example1", "example2");

Numeric Ranges

// Number with exclusive bounds (Draft 06+ syntax)
JsonSchema schema = new JsonSchema()
.setType(JsonType.NUMBER)
.setExclusiveMinimum(0) // > 0 (not >=)
.setExclusiveMaximum(100) // < 100 (not <=)
.setMultipleOf(0.5);
Draft 04 vs Draft 2020-12

In Draft 04, exclusiveMaximum and exclusiveMinimum were boolean flags used with maximum and minimum. In Draft 06+, they are numeric values representing the exclusive bounds directly.

Old (Draft 04):

.setMaximum(100)
.setExclusiveMaximum(true) // Boolean

New (Draft 2020-12):

.setExclusiveMaximum(100)  // Direct numeric value

Enumerations

// Restrict to specific values
JsonSchema schema = new JsonSchema()
.setType(JsonType.STRING)
.addEnum("pending", "active", "completed", "cancelled");

// Or use const for a single value
JsonSchema statusSchema = new JsonSchema()
.setConst("active");

Arrays

// Array of strings with item constraints
JsonSchema schema = new JsonSchema()
.setType(JsonType.ARRAY)
.setItems(new JsonSchema()
.setType(JsonType.STRING)
.setMinLength(1)
)
.setMinItems(1)
.setMaxItems(10)
.setUniqueItems(true);

// Tuple validation with prefixItems (Draft 2020-12)
JsonSchema coordinateSchema = new JsonSchema()
.setType(JsonType.ARRAY)
.addPrefixItems(
new JsonSchema().setType(JsonType.NUMBER), // latitude
new JsonSchema().setType(JsonType.NUMBER) // longitude
)
.setItems(false); // No additional items allowed

Object Properties

// Object with property constraints
JsonSchema schema = new JsonSchema()
.setType(JsonType.OBJECT)
.addProperties(
new JsonSchemaProperty("name", JsonType.STRING),
new JsonSchemaProperty("email", JsonType.STRING)
.setReadOnly(true), // Draft 07+
new JsonSchemaProperty("password", JsonType.STRING)
.setWriteOnly(true) // Draft 07+
)
.addRequired("name")
.setMinProperties(1)
.setMaxProperties(10)
.setAdditionalProperties(false); // No extra properties allowed

Advanced Features

Conditional Schemas (Draft 07+)

Use if/then/else for conditional validation:

JsonSchema schema = new JsonSchema()
.setType(JsonType.OBJECT)
.addProperties(
new JsonSchemaProperty("country", JsonType.STRING),
new JsonSchemaProperty("postalCode", JsonType.STRING)
)
.setIf(new JsonSchema()
.addProperties(
new JsonSchemaProperty("country").setConst("USA")
)
)
.setThen(new JsonSchema()
.addProperties(
new JsonSchemaProperty("postalCode")
.setPattern("^[0-9]{5}$")
)
)
.setElse(new JsonSchema()
.addProperties(
new JsonSchemaProperty("postalCode")
.setPattern("^[A-Z0-9]{3,10}$")
)
);

Reusable Definitions ($defs)

Create reusable schema components:

JsonSchema schema = new JsonSchema()
.setIdUri("https://example.com/product.schema.json")
.setType(JsonType.OBJECT)
// Define reusable schemas
.addDef("address", new JsonSchema()
.setType(JsonType.OBJECT)
.addProperties(
new JsonSchemaProperty("street", JsonType.STRING),
new JsonSchemaProperty("city", JsonType.STRING),
new JsonSchemaProperty("zipCode", JsonType.STRING)
)
.addRequired("street", "city")
)
.addDef("price", new JsonSchema()
.setType(JsonType.NUMBER)
.setExclusiveMinimum(0)
.addExamples(9.99, 19.99, 99.99)
)
// Reference definitions
.addProperties(
new JsonSchemaProperty("billingAddress")
.setRef("#/$defs/address"),
new JsonSchemaProperty("shippingAddress")
.setRef("#/$defs/address"),
new JsonSchemaProperty("price")
.setRef("#/$defs/price")
);
$defs vs definitions

Draft 2020-12 uses $defs as the preferred keyword, but definitions is still supported for backward compatibility. Use addDef() for new schemas, but addDefinition() will still work.

Schema Composition

Combine schemas using logical operators:

// allOf - must match ALL schemas
JsonSchema allOfSchema = new JsonSchema()
.addAllOf(
new JsonSchema().setType(JsonType.STRING),
new JsonSchema().setMinLength(5)
);

// anyOf - must match AT LEAST ONE schema
JsonSchema anyOfSchema = new JsonSchema()
.addAnyOf(
new JsonSchema().setType(JsonType.STRING),
new JsonSchema().setType(JsonType.NUMBER)
);

// oneOf - must match EXACTLY ONE schema
JsonSchema oneOfSchema = new JsonSchema()
.addOneOf(
new JsonSchema().setMultipleOf(5),
new JsonSchema().setMultipleOf(3)
);

// not - must NOT match schema
JsonSchema notSchema = new JsonSchema()
.setNot(new JsonSchema().setType(JsonType.NULL));

Dependent Schemas (Draft 2019-09+)

Express dependencies between properties:

// If creditCard exists, billingAddress is required
JsonSchema schema = new JsonSchema()
.setType(JsonType.OBJECT)
.addProperties(
new JsonSchemaProperty("creditCard", JsonType.STRING),
new JsonSchemaProperty("billingAddress", JsonType.STRING)
)
.addDependentSchema("creditCard", new JsonSchema()
.addRequired("billingAddress")
);

// Property-level dependencies
schema.addDependentRequired("creditCard",
Arrays.asList("billingAddress", "cardholderName"));

Parsing JSON Schema

Parse existing JSON Schema documents:

import org.apache.juneau.json.*;

String jsonSchemaString = """
{
"$id": "https://example.com/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["name"]
}
""";

JsonSchema schema = JsonParser.DEFAULT.parse(jsonSchemaString, JsonSchema.class);

// Access properties
String title = schema.getTitle();
Map<String, JsonSchema> props = schema.getProperties();
JsonSchema nameProperty = schema.getProperty("name");

Serialization to Other Formats

Since these are standard Juneau beans, you can serialize to any supported format:

import org.apache.juneau.xml.*;
import org.apache.juneau.html.*;

JsonSchema schema = new JsonSchema()
.setTitle("Example")
.setType(JsonType.STRING);

// Serialize to XML
String xml = XmlSerializer.DEFAULT.serialize(schema);

// Serialize to HTML
String html = HtmlSerializer.DEFAULT.serialize(schema);

Migration Guide

From Draft 04 to Draft 2020-12

If you have existing code using Draft 04 syntax:

Property Changes:

  • id$id (use setIdUri() instead of setId())
  • definitions$defs (use addDef() instead of addDefinition())

Semantic Changes:

// OLD: Draft 04 - boolean flags
schema.setMaximum(100).setExclusiveMaximum(true);
schema.setMinimum(0).setExclusiveMinimum(true);

// NEW: Draft 2020-12 - direct numeric values
schema.setExclusiveMaximum(100);
schema.setExclusiveMinimum(0);

Backward Compatibility: The deprecated methods still work, so existing code will continue to function:

// These still work (deprecated but supported)
schema.setId("http://example.com/schema");
schema.addDefinition("myDef", new JsonSchema());

API Reference

Main Classes

Key Methods

Identity:

  • setIdUri(Object) - Set schema identifier ($id)
  • setSchemaVersionUri(Object) - Set schema version ($schema)

Metadata:

  • setTitle(String) - Human-readable title
  • setDescription(String) - Detailed description
  • addExamples(Object...) - Add example values

Type Constraints:

  • setType(JsonType) - Set expected type
  • setEnum(List) / addEnum(Object...) - Restrict to specific values
  • setConst(Object) - Require specific value

String Constraints:

  • setMinLength(Integer) / setMaxLength(Integer) - Length bounds
  • setPattern(String) - Regular expression pattern
  • setContentMediaType(String) / setContentEncoding(String) - Content metadata

Numeric Constraints:

  • setMinimum(Number) / setMaximum(Number) - Inclusive bounds
  • setExclusiveMinimum(Number) / setExclusiveMaximum(Number) - Exclusive bounds
  • setMultipleOf(Number) - Must be multiple of value

Array Constraints:

  • setItems(Object) - Schema for array items
  • setPrefixItems(JsonSchemaArray) - Tuple validation
  • setMinItems(Integer) / setMaxItems(Integer) - Size bounds
  • setUniqueItems(Boolean) - Require unique elements

Object Constraints:

  • addProperties(JsonSchemaProperty...) - Define properties
  • addRequired(String...) - Required property names
  • setMinProperties(Integer) / setMaxProperties(Integer) - Property count bounds
  • setAdditionalProperties(Object) - Allow/disallow extra properties
  • setPatternProperties(Map) - Pattern-based property matching

Composition:

  • addAllOf(JsonSchema...) - Must match all schemas
  • addAnyOf(JsonSchema...) - Must match at least one
  • addOneOf(JsonSchema...) - Must match exactly one
  • setNot(JsonSchema) - Must not match schema

Conditional:

  • setIf(JsonSchema) / setThen(JsonSchema) / setElse(JsonSchema) - Conditional validation

Reusability:

  • addDef(String, JsonSchema) - Add reusable definition ($defs)
  • setRef(Object) - Reference another schema ($ref)

Dependencies

  • Runtime: juneau-marshall (included in juneau-bean)
  • External: None
  • Part of: juneau-bean module group

Resources