if-then-else condition evaluates to true when property undefined
Hi,
I don't think if-then-else in combination with allOf is working as intended when the property specified in the condition is undefined. All conditions seem to evaluate to true and every single then block is executed. For instance using the example here: https://json-schema.org/understanding-json-schema/reference/conditionals.html
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"country": {
"enum": ["United States of America", "Canada", "Netherlands"]
}
},
"allOf": [
{
"if": {
"properties": { "country": { "const": "United States of America" } }
},
"then": {
"properties": { "postal_code": { "pattern": "[0-9]{5}(-[0-9]{4})?" } }
}
},
{
"if": {
"properties": { "country": { "const": "Canada" } }
},
"then": {
"properties": { "postal_code": { "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]" } }
}
},
{
"if": {
"properties": { "country": { "const": "Netherlands" } }
},
"then": {
"properties": { "postal_code": { "pattern": "[0-9]{4} [A-Z]{2}" } }
}
}
]
}
the following Json is successfully validated as expected:
{
"street_address": "Adriaan Goekooplaan",
"country": "Netherlands",
"postal_code": "2517 JX"
}
but if I remove the country:
{
"street_address": "Adriaan Goekooplaan",
"postal_code": "abc"
}
then I get the following list of errors showing every then was evaluated:
$.postal_code: does not match the regex pattern [0-9]{5}(-[0-9]{4})?,
$.postal_code: does not match the regex pattern [A-Z][0-9][A-Z] [0-9][A-Z][0-9],
$.postal_code: does not match the regex pattern [0-9]{4} [A-Z]{2}]
I can make country required but then I get:
$.country: is missing but it is required,
$.postal_code: does not match the regex pattern [0-9]{5}(-[0-9]{4})?,
$.postal_code: does not match the regex pattern [A-Z][0-9][A-Z] [0-9][A-Z][0-9],
$.postal_code: does not match the regex pattern [0-9]{4} [A-Z]{2}]
@danic Thanks a lot for raising the issue. This is a defect when combining if-then with allOf as they work within a different context as a standalone validator. Have you debugged into it and figured out the root cause?
Root cause seems to be in com.networknt.schema.PropertiesValidator#validate where in this case propertyNode is null and the code will return an empty list of error messages. This is interpreted further up the stack in com.networknt.schema.IfValidator#validate as a success. Adding an else in com.networknt.schema.PropertiesValidator#validate to return an error when propertyNode is null breaks quite a few unit tests. Would require somebody familiar with the code to apply a proper fix.
@danic this is interesting... I'm trying to learn json-schema and tried your test case above in some other java libraries (everitorg & vertx), plus an online one (https://www.jsonschemavalidator.net/) and they all return the same error when there is no country as this library!
properties doesn't imply a property is mandatory, so I'm thinking maybe the if passing validation into the then when there is no country is correct? If I add "required" to the if blocks...
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"country": {
"enum": ["United States of America", "Canada", "Netherlands"]
}
},
"allOf": [
{
"if": {
"properties": { "country": { "const": "United States of America" } },
"required": ["country"]
},
"then": {
"properties": { "postal_code": { "pattern": "[0-9]{5}(-[0-9]{4})?" } }
}
},
{
"if": {
"properties": { "country": { "const": "Canada" } },
"required": ["country"]
},
"then": {
"properties": { "postal_code": { "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]" } }
}
},
{
"if": {
"properties": { "country": { "const": "Netherlands" } },
"required": ["country"]
},
"then": {
"properties": { "postal_code": { "pattern": "[0-9]{4} [A-Z]{2}" } }
}
}
]
}
then I find that this does pass validation without any errors
{
"street_address": "Adriaan Goekooplaan",
"postal_code": "abc"
}
I see this is called out as expected behaviour in the documentation at the end of this section: https://json-schema.org/understanding-json-schema/reference/conditionals.html#if-then-else "Note. The “required” keyword is necessary in the “if” schemas or they would all apply if the “country” is not defined"
@gareth-robinson Good findings. When combining multiple validators together, the situation is much more complicated. The specification team needs to manage the complicity, otherwise, the specification would be really hard to implement.
As @gareth-robinson noted, this is expected behavior as explained in the example's source.