I'm trying to create a JSON schema to validate YAML for some VSCode intellisense. What I'm trying to do is choose the correct subschema to use for a property in the main schema based on an adjacent key's value.
Some JSON examples:
[
{
"name": "doesntmatter",
"matchMe": "stringToMatch:123whatever",
"mergeMe": {
"key1": "value1",
"key2": "value2"
}
}
]
[
{
"name": "doesntmatter",
"matchMe": "anotherStringToMatch:123whatever",
"mergeMe": {
"anotherKey": "valueSomething",
"anotherKey2": "cheese"
}
}
]
So I need to choose the correct schemas for the mergeMe
objects based on the substring match of matchMe
. After following a bunch of answers, I'm at a point where I can either make it match multiple, and error my linter, or match none, but an online validator says it's ok (except nothing matches as the required
fields aren't triggering).
I moved my sub-schemas to be merged into definitions
to reference them, and then used an if/then to match. That worked with one, but then I tried to expand it to do the tree matching, and I can't get that to work. Someone said that I should wrap my if/thens in an allOf
(I'm not sure why that would work since surely not all of them would match?). Changing it to an anyOf
makes none of them match and I get no intellisense. Nor do I really understand why I should wrap single if/thens or thens in allOf
s.
The idea is that based on the pattern it uses a definitions
schema to match the mergeMe
property, but the conditional logic isn't quite right. Thinned schema below:
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/example.json",
"type": "array",
"title": "The root schema",
"description": "The root schema comprises the entire JSON document.",
"default": [],
"additionalItems": true,
"definitions": {
"stringToMatch": {
"$id": "#/definitions/stringToMatch",
"type": "object",
"properties": {
"key1": {
"type": "string"
}
},
"required": [
"key1"
],
"additionalProperties": true
},
"anotherStringToMatch": {
"$id": "#/definitions/anotherStringToMatch",
"type": "object",
"properties": {
"key2": {
"type": "string"
}
},
"required": [
"key2"
],
"additionalProperties": true
}
},
"items": {
"$id": "#/items",
"type": "object",
"title": "main schema",
"description": "An explanation about the purpose of this instance.",
"default": {},
"examples": [],
"required": [
"name",
"matchMe",
"mergeMe"
],
"properties": {
"name": {
"$id": "#/items/name",
"type": "string",
"title": "The name schema",
"description": "An explanation about the purpose of this instance.",
"default": "",
"examples": []
},
"matchMe": {
"$id": "#/items/matchMe",
"type": "string",
"title": "The matchMe schema",
"description": "An explanation about the purpose of this instance.",
"default": "",
"examples": []
}
},
"allOf": [
{
"if": {
"properties": {
"matchMe": {
"pattern": "^stringToMatch:[0-9.]+"
}
}
},
"then": {
"allOf": [
{
"type": "object",
"properties": {
"mergeMe": {
"$ref": "#/definitions/stringToMatch"
}
}
}
]
}
},
{
"if": {
"properties": {
"gear": {
"pattern": "^anotherStringToMatch:[0-9.]+"
}
}
},
"then": {
"allOf": [
{
"type": "object",
"properties": {
"mergeMe": {
"$ref": "#/definitions/anotherStringToMatch"
}
}
}
]
}
}
],
"additionalProperties": true
}
}
What I want in JS would look something like
const schema = { name, matchMe }
if (matchMe == "string1") schema.mergeMe = ...subschema1;
else if (...)
else if (...)
but I just can't really work it out. Can someone help?
Edit: jsonschema.dev playground - the idea being if I specify the food as prefixed by "fruit" I have to give it "pips" and "berry", whereas if I specify "vegetable" I have to give it a totally differet schema, and they don't overlap. https://jsonschema.dev/s/pHzGo