Applying Subschemas Conditionally not working as expected using http://json-schema.org/ example
Hi, following example is not working as expected: (very bottom of the page) http://json-schema.org/understanding-json-schema/reference/conditionals.html#id7
{ "type": "object", "properties": { "restaurantType": { "enum": ["fast-food", "sit-down"] }, "total": { "type": "number" }, "tip": { "type": "number" } }, "anyOf": [ { "not": { "properties": { "restaurantType": { "const": "sit-down" } }, "required": ["restaurantType"] } }, { "required": ["tip"] } ] }
Following input should pass, but it is not...
{ "restaurantType": "fast-food", "total": 6.99 }
Seeing following error:
<"$: should not be valid to the schema "not" : {"properties":{"restaurantType":{"const":"sit-down"}},"required":["restaurantType"]}">
Tried an online validator and it is working there: https://www.jsonschemavalidator.net/
Thank you!
Oh the issue seems to be isolated to Draft 4..
Failed here
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
Passed here
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
I think this is one of the issues that we are using some modern keywords that v4 is not supported. I am wondering if you could submit a PR to reproduce the issue so that other developers can debug it and see what is the difference between v4 and v7. Thanks.
I can provide my unit test..
Service class
`import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SpecVersion; import com.networknt.schema.ValidationMessage; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Service;
import java.io.IOException; import java.io.InputStream; import java.util.Objects; import java.util.Set;
@Service public class JsonSchemaValidator {
private ObjectMapper objectMapper = new ObjectMapper();
public String validate(String json, String schemaName) {
final JsonSchema schema =
getJsonSchemaFromClasspath(schemaName);
if (Objects.isNull(schema)) {
throw new IllegalStateException("Schema not found: " + schemaName);
}
final JsonNode jsonNode =
getJsonNodeFromStringContent(json);
final Set<ValidationMessage> errors = schema.validate(jsonNode);
if (CollectionUtils.isEmpty(errors)) {
return "No Errors";
}
return errors.iterator().next().getMessage();
}
public String validateWithStringSchema(String json, String schemaContent) {
final JsonSchema schema =
getJsonSchemaFromStringContent(schemaContent);
if (Objects.isNull(schema)) {
throw new IllegalStateException("Schema not found");
}
final JsonNode jsonNode =
getJsonNodeFromStringContent(json);
final Set<ValidationMessage> errors = schema.validate(jsonNode);
if (CollectionUtils.isEmpty(errors)) {
return "No Errors";
}
return errors.iterator().next().getMessage();
}
protected JsonSchema getJsonSchemaFromStringContent(String schemaContent) {
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
return factory.getSchema(schemaContent);
}
protected JsonSchema getJsonSchemaFromClasspath(String name) {
JsonSchemaFactory factory =
JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
InputStream is = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(name);
return factory.getSchema(is);
}
protected JsonNode getJsonNodeFromStringContent(String content) {
try {
return objectMapper.readTree(content);
} catch (JsonProcessingException e) {
throw new IllegalStateException(e.getMessage());
}
}
}`
Test
`import com.cdm.lend.leadgen.domain.demo.JsonSchemaValidator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.util.StreamUtils;
import java.nio.charset.Charset;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(MockitoExtension.class) class JsonSchemaValidatorV2Test {
private JsonSchemaValidator schemaValidator;
private ResourceLoader resourceLoader;
@BeforeEach
void setUp() {
schemaValidator = new JsonSchemaValidator();
resourceLoader = new DefaultResourceLoader();
}
@Test
void givenInquirySubmit_whenValidating_thenNoErrorsFound_V3() throws Exception {
final String pageSubmit =
getResourceString("models/submit_v2.json");
System.out.println(pageSubmit);
final String validateResult =
schemaValidator.validate(pageSubmit,
"models/submit_schema_v3.json");
assertThat(validateResult)
.isEqualTo("No Errors");
}
private String getResourceString(String file) throws Exception {
final Resource resource = resourceLoader.getResource(file);
return StreamUtils.copyToString(resource.getInputStream(),
Charset.defaultCharset());
}
}`
Schema
{ "type": "object", "properties": { "restaurantType": { "enum": ["fast-food", "sit-down"] }, "total": { "type": "number" }, "tip": { "type": "number" } }, "anyOf": [ { "not": { "properties": { "restaurantType": { "const": "sit-down" } }, "required": ["restaurantType"] } }, { "required": ["tip"] } ] }
Json
{ "restaurantType": "fast-food", "total": 6.99 }
@suren39 @stevehu The reason this test-case fails in Draft 4 is because the const keyword did not exist prior to Draft 6. I have verified that this test-case passes in all dialects after Draft 4.