Reputation: 3
I want to set some of the properties of a filter-item based on the value of an existing property. If the property Type
has the value Range
then the properties From
and To
should be added to the filterItem. Else the rest of the properties should be an array of FilterValues
. I tried to set the properties with if and else, but it seems like I am missing on something.
The parts of my JSON schema:
"Filter": {
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/filterItem"
},
"additionalProperties": false
}
___________________________________________________
"filterItem": {
"type": "object",
"properties": {
"AttributeCode": {
"type": "string",
"pattern": "^[a-z0-9_-]+$"
},
"Typ": {
"type": "string"
},
"if": {
"properties": {
"Typ": {
"const": "Range"
}
}
},
"then": {
"properties": {
"From": {
"type": "integer",
"minLength": 1
},
"To": {
"type": "integer",
"minLength": 1
}
},
"additionalProperties": false
},
"else": {
"properties": {
"FilterValues": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z0-9_-]+$"
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
The validator shows that the properties are not following my schema (Screenshot): validator
Upvotes: 0
Views: 2706
Reputation: 24489
There are two major issues with your schema, how you use if
-then
-else
and how you use addtionalProperties
.
if
-then
-else
JSON Schema keywords can only appear in the context of a schema. properties
defines an object whose values are a schema. properties
itself is not a schema.
"properties": {
"foo": { ... }
"if": { ... },
"then": { ... }
}
This doesn't define one property, "foo", plus a conditional. It defines three properties: "foo", "if", "then". You need to bring your conditional up a level for it to be recognized by the validator.
"properties": {
"foo": { ... }
},
"if": { ... }
"then": { ... }
Otherwise, you're using if
-then
-else
properly. It's just in the wrong place.
additionalProperties
Once you get your if
-then
-else
in the right place, you'll notice that you get a bunch of additionalProperties
errors. additionalProperties
can take into account only the properties
and patternProperties
keywords in the same place in the schema.
{
"allOf": [
{
"properties": {
"foo": { ... }
}
},
{
"properties": {
"bar": { ... }
},
"additionalProperties": false
}
]
}
/allOf/1/additionalProperties
can only take into account /allOf/1/properties
, not /allOf/0/properties
. So, { "foo": 1, "bar": 2 }
will be invalid because "foo" is not defined in /allOf/1/properties
.
There are a couple ways to deal with this. The preferred way is to simply not use additionalProperties
. Ignore additional properties instead of forbidding them. This is best for evolvable schemas, but depending on the domain, it's not always feasible.
To use additionalProperties
effectively, you need all possible property names under the same properties
keyword.
"properties": {
"foo": { ... },
"bar": { ... }
},
"required": ["foo"],
"additionalProperties": false,
"if": {
"properties": {
"foo": { "const": 1 }
},
"required": ["foo"]
},
"then": { "required": ["bar"] }
This is one possible approach. "bar" is only expected if "foo" is 1, but "bar" is defined at the top and only required if the conditional passes. Not only does this make additionalProperties
work as you expect, it's also easier to read because all of the property definitions are in one place and the conditional schemas are minimal.
At this point you might be concerned that including "bar" when "foo" is not 1 does not throw an error. Again, I'd encourage you to ignore the additional property rather than forbid it if your domain allows. But, if you really need to you can use else
to forbid the extra field.
"else": { "not": { "required": ["bar"] } }
Be careful not to be confused by the words in that schema. It doesn't mean "not required" which sounds like "optional", it means "forbidden".
Upvotes: 1