I understand that this kind of JSON structure isn’t very good, but this is merely for myself for convenience while still verifying that what I create is valid.
The goal JSON files that SHOULD validate are the following;
[
[ "string1" ]
]
[
[ "string1", true ]
]
[
[ "string1", true, "string2" ]
]
Each of these inner arrays MAY additionally contain another array as the final item. Said array will simply have the same validation as the root array ("$ref": "#"
). There MUST be at least 1 item in the root array.
This means that these JSON files SHOULD NOT validate;
[
]
[
[ "string1", [] ]
]
Declaring which types are allowed in the array items is fairly easy in a schema file, and I’ve done so right here;
{
"title": "",
"type": "array",
"minItems": 1,
"items": {
"type": "array",
"items": {
"type": [ "string", "boolean", "array" ],
"anyOf": [
{
"type": "string"
},
{
"type": "boolean"
},
{
"type": "array",
"$ref": "#"
}
]
}
}
}
Unfortunately, this obviously does not validate the exact order and amount of the types. Is something like that even possible with something like the oneOf
property? Maybe some combination of if
and then
?
It looks like you’re headed the right direction. You have the basic structure, but there are a few redundancies I’d like to point out.
{
"title": "",
"type": "array",
"minItems": 1,
"items": {
"type": "array",
"items": {
"type": [ "string", "boolean", "array" ], // [1]
"anyOf": [ // [2]
{
"type": "string"
},
{
"type": "boolean"
},
{
"type": "array", // [3]
"$ref": "#"
}
]
}
}
}
- This is just specifying the same as the
oneOf
. It’s not adding any extra validation and can be removed. anyOf
isn’t going to help with specifying order. This will allow these elements in any order. More on how to ensure order below.- Since the target of the
$ref
(the root) already declarestype
, this is unnecessary and can be removed.
Also, I’m not sure what version of JSON Schema you’re using. The keywords I tell you to use will depend on this. I’ll answer with draft 2020-12 keywords.
Item order
If you know the length of the inner array, you can use the prefixItems
keyword to specify each item in an index-based fashion. Then items
will address any other items.
{
"type": "array",
"prefixItems": [
{ "type": "boolean" },
{ "type": "string" },
{ "type": "boolean" }
],
"items": { "type": "integer" }
}
This schema will require a boolean, then a string, then a boolean, and then any number of integers. These are all valid:
[ true, "string", false, 1, 3, 5 ]
[ true, "string" ]
[ true, "string", false ]
These are invalid:
[ true, "string", 1, 2, 3 ]
Note that prefixItems
doesn’t require that there are three items, just that the items that are there match those subschemas.
Unfortunately, there’s not a way to specify a schema for the last item. So if the array can be any length, then you’re a bit out of luck.
A verbose option
If you don’t know how many items you’re going have, but you know there’s an upper limit, you can do something like this:
{
"title": "",
"type": "array",
"minItems": 1,
"items": {
"type": "array",
"anyOf": [
{
"prefixItems": [
{ "type": "string" }
]
},
{
"prefixItems": [
{ "type": "string" },
{ "type": "boolean" }
]
},
{
"prefixItems": [
{ "type": "string" },
{ "type": "boolean" },
{ "type": "string" }
]
}
],
"unevaluatedItems": { "$ref": "#" }
}
}
The anyOf
subschemas build up the options one item at a time. You only have to match against one of them, then unevaluatedItems
operates like items
from above (there’s a technical reason you need the different keyword, but that’s a blog post worth of explanation).
This will validate all of the ones you have in your question and these:
[ "string", [ ["string"] ] ]
[ "string", false, [ ["string"] ] ]
[ "string", false, "string", [ ["string"] ] ]