I'm designing a JSON schema with a recursive element. This element is not at the root, but in a subschema:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "http://example.org/json-schema/struct.schema.json",
"type": "array",
"items": {
"title": "Structure",
"type": "object",
"properties": {
"id": {
"title": "ID",
"type": "string"
},
"label": {
"title": "Label",
"type": "string"
},
"children": {
"$anchor": "children",
"title": "Children",
"type": "array",
"items": {
"title": "Structure Item",
"type": "object",
"properties": {
"id": {
"title": "ID",
"type": "string"
},
"href": {
"title": "Reference",
"type": "string"
},
"label": {
"title": "Structure Item Label",
"type": "string"
},
"sort_label": {
"title": "Sort Label",
"type": "string"
},
"children": {"$ref": "children"}
}
},
"anyOf": [
{"required": ["id", "href"]},
{"required": ["id", "children"]}
]
}
},
"required": ["id", "label", "children"]
}
}
When I try to compile the schema, I either get an error (with an online validator), or get caught in a loop (wit python-fastjsonschema
), due to the recursion in items.properties.children
.
From the examples in https://json-schema.org/understanding-json-schema/structuring#recursion it is not clear to me what is allowed for recursion and what is not. Recursive $ref
s inside $def
s are not allowed, but $ref
s to the root anchor are listed in a valid example. They all create a loop (that is indeed the purpose of recursion!). I thought that $ref
to a non-root anchor would be OK but it's clearly not.
I also tried to use a non-named anchor as in "children": {"$ref": "#/items/properties/children"}
, and the schema compiles, but throws an error on validation.
How shall I model this schema?
You need to reference the anchor with a #
symbol.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "http://example.org/json-schema/struct.schema.json",
"type": "array",
"items": {
"title": "Structure",
"type": "object",
"properties": {
"id": {
"title": "ID",
"type": "string"
},
"label": {
"title": "Label",
"type": "string"
},
"children": {
"$anchor": "children",
"title": "Children",
"type": "array",
"items": {
"title": "Structure Item",
"type": "object",
"properties": {
"id": {
"title": "ID",
"type": "string"
},
"href": {
"title": "Reference",
"type": "string"
},
"label": {
"title": "Structure Item Label",
"type": "string"
},
"sort_label": {
"title": "Sort Label",
"type": "string"
},
"children": {"$ref": "#children"}
}
},
"anyOf": [
{"required": ["id", "href"]},
{"required": ["id", "children"]}
]
}
},
"required": ["id", "label", "children"]
}
}
This is a passing instance.
[
{
"id": "",
"label": "",
"children": [
{
"id": "",
"children": []
},
{
"id": "",
"href": ""
}
]
}
]
You may want to reconsider your anyOf
required constraints because at the moment with anyOf
, they are not really required in the way I think you want them required. Try oneOf