I have a JSON Schema, in the schema - when the property type
is equal to menu
, then the property default
should be an integer or a string called "default". The problem is, the schema is throwing a warning / error that the default
property should be an integer, even when type
is not "menu".
(There are properties in the JSON named "type" and "default", not to be confused with the JSON schema type & default keys).
The Schema cut down to the relevant properties:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"title": "HubSpot Module",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "The type of field, see [field types](https://developers.hubspot.com/en/docs/cms/building-blocks/module-theme-fields#field-types) for documentation on all field types."
},
"default": {
"type": ["string", "integer", "array", "boolean", "null", "number", "object"],
"description": "Default value for the field.",
"if": {
"properties": {
"type": {
"const": "menu"
}
}
},
"then": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string",
"enum": [
"default"
]
}
],
"description": "The menu ID for the menu. The default value of null, defaults to the default menu under navigation.",
"default": "null"
},
"else": {
"anyOf": [
{
"type": ["string", "integer", "array", "boolean", "null", "number", "object"]
}
]
}
}
},
"required": [
"name",
"label",
"type"
]
}
}
It works when those conditions are met, e.g.
[
{
"label": "Primary menu field",
"name": "primary_menu_field",
"type": "menu",
"default": 123
}
]
[
{
"label": "Primary menu field",
"name": "primary_menu_field",
"type": "menu",
"default": "default"
}
]
neither of these throw warnings. However the following throws the warning: Incorrect type. Expected "integer".
[
{
"name": "boolean_field",
"label": "Boolean field",
"required": false,
"locked": false,
"type": "boolean",
"inline_help_text": "",
"help_text": "",
"default": false
}
]
Despite the type
property not being named "menu". Hovering over the "default" property VSCode does give me the definition I have for it.
It seems like the if
statement is always being evaluated as true - when it should only be evaluated if the value of property type
is menu
, is there something I'm missing?
CodePudding user response:
Your if
isn't working as expected because it's in the wrong place and it's insufficiently constrained. You have the if
in the "default" property schema which means the if
schema will apply to the value of the "default" property. So, if "default" is 123
, then the if
schema,
{
"properties": {
"type": { "const": "menu" }
}
}
will be evaluated against 123
. Since 123
is a number, the properties
keyword is ignored and the schema will pass validation and the then
schema will apply. This is what I meant by if
being under constrained. When using if
you need to check for missing data or data of the wrong type.
{
"type": "object",
"properties": {
"type": { "const": "menu" }
},
"required": ["type"]
}
Now you'll get an error message when validating 123
against this schema and hopefully figure out that your if
/then
is in the wrong place. The if
needs to apply to the object, you need to move it up to that level in the schema. That will make your if
evaluated as expected. Don't forget to update the then
as well since you moved the schema. I also needs to be defined at the object level rather than the property level.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "array",
"items": {
"type": "object",
"properties": {
"type": { "type": "string" }
},
"required": ["name", "label", "type"],
"if": {
"type": "object",
"properties": {
"type": { "const": "menu" }
},
"required": ["type"]
},
"then": {
"properties": {
"default": {
"anyOf": [
{ "type": "integer" },
{ "const": "default" }
]
}
}
}
}
}
CodePudding user response:
Your if
/then
/else
is under the default
property, so it's looking for an object under default
, rather than one level up where you want it.
Also, the properties
check in the if
condition will always be true if the property doesn't actually exist, so to ensure it does, you can add a "required": <propertyname>
alongside it.
(Orthogonally, your type
check doesn't really do anything since you include all valid types in the list. You might as well just leave it out to indicate "this can be any type".)