fixes #456 OneOf only validate the first sub schema
diff --git a/src/main/java/com/networknt/schema/OneOfValidator.java b/src/main/java/com/networknt/schema/OneOfValidator.java
index 283875e..5e48902 100644
--- a/src/main/java/com/networknt/schema/OneOfValidator.java
+++ b/src/main/java/com/networknt/schema/OneOfValidator.java
@@ -17,27 +17,19 @@
package com.networknt.schema;
import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.NullNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.networknt.schema.utils.JsonNodeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public class OneOfValidator extends BaseJsonValidator implements JsonValidator {
-
private static final Logger logger = LoggerFactory.getLogger(OneOfValidator.class);
private final List<ShortcutValidator> schemas = new ArrayList<ShortcutValidator>();
- private final ValidationContext validationContext;
-
private static class ShortcutValidator {
private final JsonSchema schema;
private final Map<String, String> constants;
- private final ValidationContext validationContext;
ShortcutValidator(JsonNode schemaNode, JsonSchema parentSchema,
ValidationContext validationContext, JsonSchema schema) {
@@ -45,7 +37,6 @@
JsonSchema resolvedRefSchema = refNode != null && refNode.isTextual() ? RefValidator.getRefSchema(parentSchema, validationContext, refNode.textValue()).getSchema() : null;
this.constants = extractConstants(schemaNode, resolvedRefSchema);
this.schema = schema;
- this.validationContext = validationContext;
}
private Map<String, String> extractConstants(JsonNode schemaNode, JsonSchema resolvedRefSchema) {
@@ -130,7 +121,6 @@
JsonSchema childSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), childNode, parentSchema);
schemas.add(new ShortcutValidator(childNode, parentSchema, validationContext, childSchema));
}
- this.validationContext = validationContext;
parseErrorCode(getValidatorType().getErrorCodeKey());
}
@@ -163,49 +153,33 @@
continue;
}*/
- //Check to see if it is already validated.
- if(!childErrors.isEmpty() && JsonNodeUtil.matchOneOfTypeNode(schemaNode,TypeFactory.getValueNodeType(node, this.validationContext.getConfig()))){
- continue;
- }
-
// get the current validator
JsonSchema schema = validator.schema;
-
- //Skip the validation when the current node is oneOf type and it is not equal to schemaType.
- if (JsonNodeUtil.matchOneOfTypeNode(schemaNode, TypeFactory.getValueNodeType(node, this.validationContext.getConfig())) &&
- !JsonNodeUtil.equalsToSchemaType(node, schema, this.validationContext.getConfig()) && !(JsonType.UNKNOWN.equals(JsonNodeUtil.getSchemaJsonType(schema)))) {
- continue;
- }
-
if (!state.isWalkEnabled()) {
schemaErrors = schema.validate(node, rootNode, at);
} else {
schemaErrors = schema.walk(node, rootNode, at, state.isValidationEnabled());
}
-
// check if any validation errors have occurred
if (schemaErrors.isEmpty()) {
// check whether there are no errors HOWEVER we have validated the exact validator
if (!state.hasMatchedNode())
continue;
- else
- numberOfValidSchema++;
+
+ numberOfValidSchema++;
}
childErrors.addAll(schemaErrors);
}
+
+
// ensure there is always an "OneOf" error reported if number of valid schemas is not equal to 1.
- if (numberOfValidSchema > 1) {
- // check if the parent schema declares the fields as nullable
- if (!JsonType.NULL.equals(TypeFactory.getValueNodeType(node, this.validationContext.getConfig())) ||
- !JsonNodeUtil.isNodeNullable(parentSchema.getSchemaNode(), this.validationContext.getConfig()) &&
- !JsonNodeUtil.isChildNodeNullable((ArrayNode) schemaNode, this.validationContext.getConfig())) {
- final ValidationMessage message = getMultiSchemasValidErrorMsg(at);
- if (failFast) {
- throw new JsonSchemaException(message);
- }
- errors.add(message);
+ if(numberOfValidSchema > 1){
+ final ValidationMessage message = getMultiSchemasValidErrorMsg(at);
+ if( failFast ) {
+ throw new JsonSchemaException(message);
}
+ errors.add(message);
}
// ensure there is always an "OneOf" error reported if number of valid schemas is not equal to 1.
else if (numberOfValidSchema < 1) {
@@ -262,7 +236,7 @@
}
return ValidationMessage.of(getValidatorType().getValue(), ValidatorTypeCode.ONE_OF ,
- at, String.format("but more than one schemas {%s} are valid ",msg));
+ at, String.format("but more than one schemas {%s} are valid ",msg));
}
@Override
diff --git a/src/test/java/com/networknt/schema/Issue456Test.java b/src/test/java/com/networknt/schema/Issue456Test.java
new file mode 100644
index 0000000..c96c389
--- /dev/null
+++ b/src/test/java/com/networknt/schema/Issue456Test.java
@@ -0,0 +1,48 @@
+package com.networknt.schema;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStream;
+import java.util.Set;
+
+public class Issue456Test {
+
+ protected JsonSchema getJsonSchemaFromStreamContentV7(InputStream schemaContent) {
+ JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
+ return factory.getSchema(schemaContent);
+ }
+
+ protected JsonNode getJsonNodeFromStreamContent(InputStream content) throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ return mapper.readTree(content);
+ }
+
+ @Test
+ public void shouldWorkT2() throws Exception {
+ String schemaPath = "/schema/issue456-v7.json";
+ String dataPath = "/data/issue456-T2.json";
+ String dataT3Path = "/data/issue456-T3.json";
+ InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
+ JsonSchema schema = getJsonSchemaFromStreamContentV7(schemaInputStream);
+ InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
+ JsonNode node = getJsonNodeFromStreamContent(dataInputStream);
+ Set<ValidationMessage> errors = schema.validate(node);
+ Assertions.assertEquals(0, errors.size());
+ }
+
+ @Test
+ public void shouldWorkT3() throws Exception {
+ String schemaPath = "/schema/issue456-v7.json";
+ String dataPath = "/data/issue456-T3.json";
+ InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
+ JsonSchema schema = getJsonSchemaFromStreamContentV7(schemaInputStream);
+ InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
+ JsonNode node = getJsonNodeFromStreamContent(dataInputStream);
+ Set<ValidationMessage> errors = schema.validate(node);
+ Assertions.assertEquals(0, errors.size());
+ }
+
+}
diff --git a/src/test/resources/data/issue456-T2.json b/src/test/resources/data/issue456-T2.json
new file mode 100644
index 0000000..b39deea
--- /dev/null
+++ b/src/test/resources/data/issue456-T2.json
@@ -0,0 +1,7 @@
+{
+ "id": "abc",
+ "details": {
+ "__typename": "T2",
+ "name": "Bob"
+ }
+}
diff --git a/src/test/resources/data/issue456-T3.json b/src/test/resources/data/issue456-T3.json
new file mode 100644
index 0000000..a4688b7
--- /dev/null
+++ b/src/test/resources/data/issue456-T3.json
@@ -0,0 +1,7 @@
+{
+ "id": "def",
+ "details": {
+ "__typename": "T3",
+ "description": "Something"
+ }
+}
diff --git a/src/test/resources/schema/issue456-v7.json b/src/test/resources/schema/issue456-v7.json
new file mode 100644
index 0000000..46fbe08
--- /dev/null
+++ b/src/test/resources/schema/issue456-v7.json
@@ -0,0 +1,44 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://example.com/issue-456.json",
+ "title": "OneOf validates first child only",
+ "description": "Test description",
+ "type": "object",
+ "properties": {
+ "id": "string",
+ "details": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "__typename": {
+ "const": "T2"
+ }
+ },
+ "required": [
+ "name",
+ "__typename"
+ ]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "__typename": {
+ "const": "T3"
+ }
+ },
+ "required": [
+ "description",
+ "__typename"
+ ]
+ }
+ ]
+ }
+ }
+}