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);
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")
);
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
(usesetIdUri()
instead ofsetId()
)definitions
→$defs
(useaddDef()
instead ofaddDefinition()
)
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
JsonSchema
- Main schema beanJsonSchemaProperty
- Property schema (extends JsonSchema)JsonType
- Enum for JSON typesJsonSchemaRef
- Schema reference ($ref)JsonSchemaArray
- Array of schemasJsonSchemaMap
- Map of schemas
Key Methods
Identity:
setIdUri(Object)
- Set schema identifier ($id)setSchemaVersionUri(Object)
- Set schema version ($schema)
Metadata:
setTitle(String)
- Human-readable titlesetDescription(String)
- Detailed descriptionaddExamples(Object...)
- Add example values
Type Constraints:
setType(JsonType)
- Set expected typesetEnum(List)
/addEnum(Object...)
- Restrict to specific valuessetConst(Object)
- Require specific value
String Constraints:
setMinLength(Integer)
/setMaxLength(Integer)
- Length boundssetPattern(String)
- Regular expression patternsetContentMediaType(String)
/setContentEncoding(String)
- Content metadata
Numeric Constraints:
setMinimum(Number)
/setMaximum(Number)
- Inclusive boundssetExclusiveMinimum(Number)
/setExclusiveMaximum(Number)
- Exclusive boundssetMultipleOf(Number)
- Must be multiple of value
Array Constraints:
setItems(Object)
- Schema for array itemssetPrefixItems(JsonSchemaArray)
- Tuple validationsetMinItems(Integer)
/setMaxItems(Integer)
- Size boundssetUniqueItems(Boolean)
- Require unique elements
Object Constraints:
addProperties(JsonSchemaProperty...)
- Define propertiesaddRequired(String...)
- Required property namessetMinProperties(Integer)
/setMaxProperties(Integer)
- Property count boundssetAdditionalProperties(Object)
- Allow/disallow extra propertiessetPatternProperties(Map)
- Pattern-based property matching
Composition:
addAllOf(JsonSchema...)
- Must match all schemasaddAnyOf(JsonSchema...)
- Must match at least oneaddOneOf(JsonSchema...)
- Must match exactly onesetNot(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 injuneau-bean
) - External: None
- Part of:
juneau-bean
module group
Resources
- JSON Schema 2020-12 Specification
- JSON Schema Validation
- Understanding JSON Schema
- JSON Schema Store - Collection of common schemas