I want to validate objects like this
{
type: "user",
data: {id: 1},
}
and
{
type: "account",
data: {uuid: "xxxx"},
}
I thought I can write schema like this
{
type: "object",
properties: {
type: {enum: ["user", "account"]},
data: {
"if": {properties: {type: {const: "user"}}},
"then": {
type: "object",
properties: {
id: {type: "number"}
}
},
"else": {
type: "object",
properties: {
uuid: {type: "string"}
}
},
}
}
}
but it looks like I can't refer to root type
field from context of data
field. So I have two questions. Can I use if
deeper than root? If I can then how to refer to parent or root object from nested object? I'm using node ajv
for validation.
You're correct, you cannot apply validation to a different part of your data than where your subschems is being applied. Let me explain a little.
The first thing that happens when processing a JSON Schema is the Schema as a whole is "applied" to the instance as a whole.
properties
is an applicator keyword, in that it doesn't assert any validation rules by itself. The VALUES of a properties
object are subschemas (which are Schemas in their own right) which are APPLIED to the instance location value when the associated KEY matches.
To give you an exanple, in your Schema, the subschema at properties > type
is applied to instance location type
, which means the VALUE of the object key type
. If type
didn't exist in your data, the subschema in your Schema wouldn't do anything (it wouldn't be applied anywhere).
This is broadly the processing model of a Schema against an instance. Hopefully this explains why you can't work in the way you're expecting. However it's still possible to achive what you want.
then
and else
are conditional applicator keywords, and so need to be applied following the same model.
What you need to do is use if
, then
, else
at the top level, but have deep / nested application of the validation you want to do.
Here's a demo with a new Schema and instance https://jsonschema.dev/s/sejHF
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"properties": {
"type": {
"enum": [
"user",
"account"
]
},
"data": {}
},
"if": {
"properties": {
"type": {
"const": "user"
}
}
},
"then": {
"type": "object",
"properties": {
"data": {
"properties": {
"id": {
"type": "number"
}
}
}
}
},
"else": {
"type": "object",
"properties": {
"data": {
"properties": {
"uuid": {
"type": "string"
}
}
}
}
}
}
If you also want to make sure that id
is present when type
is user
, then you also need to add required: ['id']
in the then
subschema. (You'll need to do similar in the else
clause if you want to check for uuid
too.