Composition keywords let you define shared sub-schemas and combine validation rules.
Supported keywords
| Keyword | What it does |
|---|
$defs / $ref | Define and reuse shared sub-schemas |
allOf | All subschemas must match |
anyOf / oneOf | Exactly one subschema must match |
not | The subschema must not match |
Reuse with $defs and $ref
Define shared sub-schemas once, then reference them.
{
"$defs": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"zip": { "type": "string", "pattern": "^[0-9]{5}$" }
},
"required": ["street", "city", "zip"],
"additionalProperties": false
}
},
"type": "object",
"properties": {
"billing": { "$ref": "#/$defs/address" },
"shipping": { "$ref": "#/$defs/address" }
},
"required": ["billing", "shipping"],
"additionalProperties": false
}
$ref support is for local references (for example #/$defs/...).
allOf
Combine constraints that must all apply.
{
"allOf": [
{
"type": "object",
"properties": { "name": { "type": "string" } },
"required": ["name"]
},
{
"type": "object",
"properties": { "email": { "type": "string", "format": "email" } },
"required": ["email"]
}
]
}
Pydantic example is omitted because there is no direct allOf keyword mapping.
anyOf / oneOf
Both anyOf and oneOf behave the same way: exactly one subschema must match. Use a discriminator field plus const to make branches explicit.
{
"oneOf": [
{
"type": "object",
"properties": {
"tool": { "const": "search" },
"query": { "type": "string", "minLength": 1 }
},
"required": ["tool", "query"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"tool": { "const": "lookup" },
"id": { "type": "integer" }
},
"required": ["tool", "id"],
"additionalProperties": false
}
]
}
not
Reject values that match a subschema:
{
"type": "object",
"properties": {
"value": {
"type": "string",
"not": { "const": "" }
}
},
"required": ["value"],
"additionalProperties": false
}
Pydantic and Zod examples are omitted here because there is no direct keyword mapping for JSON Schema not.
This accepts any non-empty string.
Recursive schemas
Recursive local references are supported:
{
"$defs": {
"node": {
"type": "object",
"properties": {
"name": { "type": "string" },
"children": {
"type": "array",
"items": { "$ref": "#/$defs/node" },
"maxItems": 10
}
},
"required": ["name", "children"],
"additionalProperties": false
}
},
"type": "object",
"properties": {
"root": { "$ref": "#/$defs/node" }
},
"required": ["root"],
"additionalProperties": false
}