diff options
author | Justin Tay <49700559+justin-tay@users.noreply.github.com> | 2024-03-19 21:14:51 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-19 09:14:51 -0400 |
commit | aeb48e25927c4f622f93f3accdc87de1f9c1daa1 (patch) | |
tree | 09d4020cb12fe9896c734a7db7b6f99d5190b943 | |
parent | 3416e28db90696e5b091f7b8f99e00d93ad279fe (diff) | |
download | json-schema-validator-aeb48e25927c4f622f93f3accdc87de1f9c1daa1.tar.gz |
Walk items schema instead of walking instance data (#993)
14 files changed, 839 insertions, 63 deletions
diff --git a/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java b/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java index 62262ba..48b5f7d 100644 --- a/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java +++ b/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java @@ -165,7 +165,7 @@ public class AdditionalPropertiesValidator extends BaseJsonValidator { @Override
public Set<ValidationMessage> walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation, boolean shouldValidateSchema) {
- if (shouldValidateSchema) {
+ if (shouldValidateSchema && node != null) {
return validate(executionContext, node, rootNode, instanceLocation);
}
diff --git a/src/main/java/com/networknt/schema/DynamicRefValidator.java b/src/main/java/com/networknt/schema/DynamicRefValidator.java index 6f72993..7d68d23 100644 --- a/src/main/java/com/networknt/schema/DynamicRefValidator.java +++ b/src/main/java/com/networknt/schema/DynamicRefValidator.java @@ -113,6 +113,22 @@ public class DynamicRefValidator extends BaseJsonValidator { .arguments(schemaNode.asText()).build();
throw new InvalidSchemaRefException(validationMessage);
}
+ if (node == null) {
+ // Check for circular dependency
+ SchemaLocation schemaLocation = refSchema.getSchemaLocation();
+ JsonSchema check = refSchema;
+ boolean circularDependency = false;
+ while (check.getEvaluationParentSchema() != null) {
+ check = check.getEvaluationParentSchema();
+ if (check.getSchemaLocation().equals(schemaLocation)) {
+ circularDependency = true;
+ break;
+ }
+ }
+ if (circularDependency) {
+ return Collections.emptySet();
+ }
+ }
return refSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
}
diff --git a/src/main/java/com/networknt/schema/ItemsValidator.java b/src/main/java/com/networknt/schema/ItemsValidator.java index 7aede16..7912ebd 100644 --- a/src/main/java/com/networknt/schema/ItemsValidator.java +++ b/src/main/java/com/networknt/schema/ItemsValidator.java @@ -203,25 +203,125 @@ public class ItemsValidator extends BaseJsonValidator { @Override public Set<ValidationMessage> walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation, boolean shouldValidateSchema) { - HashSet<ValidationMessage> validationMessages = new LinkedHashSet<>(); - if (node instanceof ArrayNode) { - ArrayNode arrayNode = (ArrayNode) node; - JsonNode defaultNode = null; - if (this.validationContext.getConfig().getApplyDefaultsStrategy().shouldApplyArrayDefaults() - && this.schema != null) { - defaultNode = getDefaultNode(this.schema); + Set<ValidationMessage> validationMessages = new LinkedHashSet<>(); + boolean collectAnnotations = collectAnnotations(); + + // Add items annotation + if (collectAnnotations || collectAnnotations(executionContext)) { + if (this.schema != null) { + // Applies to all + executionContext.getAnnotations() + .put(JsonNodeAnnotation.builder().instanceLocation(instanceLocation) + .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation) + .keyword(getKeyword()).value(true).build()); + } else if (this.tupleSchema != null) { + // Tuples + int items = node.isArray() ? node.size() : 1; + int schemas = this.tupleSchema.size(); + if (items > schemas) { + // More items than schemas so the keyword only applied to the number of schemas + executionContext.getAnnotations() + .put(JsonNodeAnnotation.builder().instanceLocation(instanceLocation) + .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation) + .keyword(getKeyword()).value(schemas).build()); + } else { + // Applies to all + executionContext.getAnnotations() + .put(JsonNodeAnnotation.builder().instanceLocation(instanceLocation) + .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation) + .keyword(getKeyword()).value(true).build()); + } } - int i = 0; - for (JsonNode n : arrayNode) { - if (n.isNull() && defaultNode != null) { - arrayNode.set(i, defaultNode); - n = defaultNode; + } + + if (this.schema != null) { + // Walk the schema. + if (node instanceof ArrayNode) { + int count = Math.max(1, node.size()); + ArrayNode arrayNode = (ArrayNode) node; + JsonNode defaultNode = null; + if (this.validationContext.getConfig().getApplyDefaultsStrategy().shouldApplyArrayDefaults()) { + defaultNode = getDefaultNode(this.schema); + } + for (int i = 0; i < count; i++) { + JsonNode n = arrayNode.get(i); + if (n != null) { + if (n.isNull() && defaultNode != null) { + arrayNode.set(i, defaultNode); + n = defaultNode; + } + } + walkSchema(executionContext, this.schema, n, rootNode, instanceLocation.append(i), shouldValidateSchema, validationMessages, ValidatorTypeCode.ITEMS.getValue()); + } + } else { + walkSchema(executionContext, this.schema, null, rootNode, instanceLocation.append(0), shouldValidateSchema, validationMessages, ValidatorTypeCode.ITEMS.getValue()); + } + } + else if (this.tupleSchema != null) { + int prefixItems = this.tupleSchema.size(); + for (int i = 0; i < prefixItems; i++) { + // walk tuple schema + if (node instanceof ArrayNode) { + ArrayNode arrayNode = (ArrayNode) node; + JsonNode defaultNode = null; + JsonNode n = arrayNode.get(i); + if (this.validationContext.getConfig().getApplyDefaultsStrategy().shouldApplyArrayDefaults()) { + defaultNode = getDefaultNode(this.tupleSchema.get(i)); + } + if (n != null) { + if (n.isNull() && defaultNode != null) { + arrayNode.set(i, defaultNode); + n = defaultNode; + } + } + walkSchema(executionContext, this.tupleSchema.get(i), n, rootNode, instanceLocation.append(i), + shouldValidateSchema, validationMessages, ValidatorTypeCode.ITEMS.getValue()); + } else { + walkSchema(executionContext, this.tupleSchema.get(i), null, rootNode, instanceLocation.append(i), + shouldValidateSchema, validationMessages, ValidatorTypeCode.ITEMS.getValue()); + } + } + if (this.additionalSchema != null) { + boolean hasAdditionalItem = false; + + int additionalItems = Math.max(1, (node != null ? node.size() : 0) - prefixItems); + for (int x = 0; x < additionalItems; x++) { + int i = x + prefixItems; + // walk additional item schema + if (node instanceof ArrayNode) { + ArrayNode arrayNode = (ArrayNode) node; + JsonNode defaultNode = null; + JsonNode n = arrayNode.get(i); + if (this.validationContext.getConfig().getApplyDefaultsStrategy().shouldApplyArrayDefaults()) { + defaultNode = getDefaultNode(this.additionalSchema); + } + if (n != null) { + if (n.isNull() && defaultNode != null) { + arrayNode.set(i, defaultNode); + n = defaultNode; + } + } + walkSchema(executionContext, this.additionalSchema, n, rootNode, instanceLocation.append(i), + shouldValidateSchema, validationMessages, PROPERTY_ADDITIONAL_ITEMS); + if (n != null) { + hasAdditionalItem = true; + } + } else { + walkSchema(executionContext, this.additionalSchema, null, rootNode, instanceLocation.append(i), + shouldValidateSchema, validationMessages, PROPERTY_ADDITIONAL_ITEMS); + } + } + + if (hasAdditionalItem) { + if (collectAnnotations || collectAnnotations(executionContext, "additionalItems")) { + executionContext.getAnnotations() + .put(JsonNodeAnnotation.builder().instanceLocation(instanceLocation) + .evaluationPath(this.additionalItemsEvaluationPath) + .schemaLocation(this.additionalItemsSchemaLocation) + .keyword("additionalItems").value(true).build()); + } } - doWalk(executionContext, validationMessages, i, n, rootNode, instanceLocation, shouldValidateSchema); - i++; } - } else { - doWalk(executionContext, validationMessages, 0, node, rootNode, instanceLocation, shouldValidateSchema); } return validationMessages; } @@ -237,36 +337,14 @@ public class ItemsValidator extends BaseJsonValidator { return result; } - private void doWalk(ExecutionContext executionContext, HashSet<ValidationMessage> validationMessages, int i, JsonNode node, - JsonNode rootNode, JsonNodePath instanceLocation, boolean shouldValidateSchema) { - if (this.schema != null) { - // Walk the schema. - walkSchema(executionContext, this.schema, node, rootNode, instanceLocation.append(i), shouldValidateSchema, validationMessages); - } - - if (this.tupleSchema != null) { - if (i < this.tupleSchema.size()) { - // walk tuple schema - walkSchema(executionContext, this.tupleSchema.get(i), node, rootNode, instanceLocation.append(i), - shouldValidateSchema, validationMessages); - } else { - if (this.additionalSchema != null) { - // walk additional item schema - walkSchema(executionContext, this.additionalSchema, node, rootNode, instanceLocation.append(i), - shouldValidateSchema, validationMessages); - } - } - } - } - private void walkSchema(ExecutionContext executionContext, JsonSchema walkSchema, JsonNode node, JsonNode rootNode, - JsonNodePath instanceLocation, boolean shouldValidateSchema, Set<ValidationMessage> validationMessages) { - boolean executeWalk = this.validationContext.getConfig().getItemWalkListenerRunner().runPreWalkListeners(executionContext, ValidatorTypeCode.ITEMS.getValue(), + JsonNodePath instanceLocation, boolean shouldValidateSchema, Set<ValidationMessage> validationMessages, String keyword) { + boolean executeWalk = this.validationContext.getConfig().getItemWalkListenerRunner().runPreWalkListeners(executionContext, keyword, node, rootNode, instanceLocation, walkSchema, this); if (executeWalk) { validationMessages.addAll(walkSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema)); } - this.validationContext.getConfig().getItemWalkListenerRunner().runPostWalkListeners(executionContext, ValidatorTypeCode.ITEMS.getValue(), node, rootNode, + this.validationContext.getConfig().getItemWalkListenerRunner().runPostWalkListeners(executionContext, keyword, node, rootNode, instanceLocation, walkSchema, this, validationMessages); } diff --git a/src/main/java/com/networknt/schema/ItemsValidator202012.java b/src/main/java/com/networknt/schema/ItemsValidator202012.java index a9cd02a..9edcb37 100644 --- a/src/main/java/com/networknt/schema/ItemsValidator202012.java +++ b/src/main/java/com/networknt/schema/ItemsValidator202012.java @@ -122,6 +122,7 @@ public class ItemsValidator202012 extends BaseJsonValidator { && this.schema != null) { defaultNode = getDefaultNode(this.schema); } + boolean evaluated = false; for (int i = this.prefixCount; i < node.size(); ++i) { JsonNode n = node.get(i); if (n.isNull() && defaultNode != null) { @@ -131,10 +132,24 @@ public class ItemsValidator202012 extends BaseJsonValidator { // Walk the schema. walkSchema(executionContext, this.schema, n, rootNode, instanceLocation.append(i), shouldValidateSchema, validationMessages); + if (n != null) { + evaluated = true; + } + } + if (evaluated) { + if (collectAnnotations() || collectAnnotations(executionContext)) { + // Applies to all + executionContext.getAnnotations() + .put(JsonNodeAnnotation.builder().instanceLocation(instanceLocation) + .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation) + .keyword(getKeyword()).value(true).build()); + } } } else { - walkSchema(executionContext, this.schema, node, rootNode, instanceLocation, shouldValidateSchema, - validationMessages); + // If the node is not an ArrayNode, eg. ObjectNode or null then the instance is null. + // The instance location starts at the end of the prefix count. + walkSchema(executionContext, this.schema, null, rootNode, instanceLocation.append(this.prefixCount), + shouldValidateSchema, validationMessages); } return validationMessages; @@ -150,7 +165,7 @@ public class ItemsValidator202012 extends BaseJsonValidator { } return result; } - + private void walkSchema(ExecutionContext executionContext, JsonSchema walkSchema, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation, boolean shouldValidateSchema, Set<ValidationMessage> validationMessages) { //@formatter:off diff --git a/src/main/java/com/networknt/schema/JsonValidator.java b/src/main/java/com/networknt/schema/JsonValidator.java index fe4b71a..158a095 100644 --- a/src/main/java/com/networknt/schema/JsonValidator.java +++ b/src/main/java/com/networknt/schema/JsonValidator.java @@ -59,6 +59,10 @@ public interface JsonValidator extends JsonSchemaWalker { @Override
default Set<ValidationMessage> walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode,
JsonNodePath instanceLocation, boolean shouldValidateSchema) {
+ if (node == null) {
+ // Note that null is not the same as NullNode
+ return Collections.emptySet();
+ }
return shouldValidateSchema ? validate(executionContext, node, rootNode, instanceLocation)
: Collections.emptySet();
}
diff --git a/src/main/java/com/networknt/schema/PrefixItemsValidator.java b/src/main/java/com/networknt/schema/PrefixItemsValidator.java index 94bd136..ccda858 100644 --- a/src/main/java/com/networknt/schema/PrefixItemsValidator.java +++ b/src/main/java/com/networknt/schema/PrefixItemsValidator.java @@ -47,8 +47,11 @@ public class PrefixItemsValidator extends BaseJsonValidator { this.tupleSchema = new ArrayList<>(); if (schemaNode instanceof ArrayNode && 0 < schemaNode.size()) { + int i = 0; for (JsonNode s : schemaNode) { - this.tupleSchema.add(validationContext.newSchema(schemaLocation, evaluationPath, s, parentSchema)); + this.tupleSchema.add(validationContext.newSchema(schemaLocation.append(i), evaluationPath.append(i), s, + parentSchema)); + i++; } } else { throw new IllegalArgumentException("The value of 'prefixItems' MUST be a non-empty array of valid JSON Schemas."); @@ -101,22 +104,49 @@ public class PrefixItemsValidator extends BaseJsonValidator { @Override public Set<ValidationMessage> walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation, boolean shouldValidateSchema) { Set<ValidationMessage> validationMessages = new LinkedHashSet<>(); - - if (this.validationContext.getConfig().getApplyDefaultsStrategy().shouldApplyArrayDefaults() - && node.isArray()) { + if (node instanceof ArrayNode) { ArrayNode array = (ArrayNode) node; - int count = Math.min(node.size(), this.tupleSchema.size()); + int count = this.tupleSchema.size(); for (int i = 0; i < count; ++i) { JsonNode n = node.get(i); - JsonNode defaultNode = getDefaultNode(this.tupleSchema.get(i)); - if (n.isNull() && defaultNode != null) { - array.set(i, defaultNode); - n = defaultNode; + if (this.validationContext.getConfig().getApplyDefaultsStrategy().shouldApplyArrayDefaults()) { + JsonNode defaultNode = getDefaultNode(this.tupleSchema.get(i)); + if (n != null) { + // Defaults only set if array index is explicitly null + if (n.isNull() && defaultNode != null) { + array.set(i, defaultNode); + n = defaultNode; + } + } } doWalk(executionContext, validationMessages, i, n, rootNode, instanceLocation, shouldValidateSchema); } - } + // Add annotation + if (collectAnnotations() || collectAnnotations(executionContext)) { + // Tuples + int items = node.isArray() ? node.size() : 1; + int schemas = this.tupleSchema.size(); + if (items > schemas) { + // More items than schemas so the keyword only applied to the number of schemas + executionContext.getAnnotations() + .put(JsonNodeAnnotation.builder().instanceLocation(instanceLocation) + .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation) + .keyword(getKeyword()).value(schemas).build()); + } else { + // Applies to all + executionContext.getAnnotations() + .put(JsonNodeAnnotation.builder().instanceLocation(instanceLocation) + .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation) + .keyword(getKeyword()).value(true).build()); + } + } + } else { + int count = this.tupleSchema.size(); + for (int i = 0; i < count; ++i) { + doWalk(executionContext, validationMessages, i, null, rootNode, instanceLocation, shouldValidateSchema); + } + } return validationMessages; } diff --git a/src/main/java/com/networknt/schema/PropertiesValidator.java b/src/main/java/com/networknt/schema/PropertiesValidator.java index a47e61c..f378905 100644 --- a/src/main/java/com/networknt/schema/PropertiesValidator.java +++ b/src/main/java/com/networknt/schema/PropertiesValidator.java @@ -18,6 +18,7 @@ package com.networknt.schema; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
+import com.fasterxml.jackson.databind.node.MissingNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.networknt.schema.annotation.JsonNodeAnnotation;
import com.networknt.schema.utils.JsonSchemaRefs;
@@ -99,7 +100,7 @@ public class PropertiesValidator extends BaseJsonValidator { if (errors == null) {
errors = new SetView<>();
}
- walkSchema(executionContext, entry, node, rootNode, instanceLocation, state.isValidationEnabled(), errors, this.validationContext.getConfig().getPropertyWalkListenerRunner(), false);
+ walkSchema(executionContext, entry, node, rootNode, instanceLocation, state.isValidationEnabled(), errors, this.validationContext.getConfig().getPropertyWalkListenerRunner());
}
// reset the complex flag to the original value before the recursive call
@@ -118,7 +119,7 @@ public class PropertiesValidator extends BaseJsonValidator { if (errors == null) {
errors = new SetView<>();
}
- walkSchema(executionContext, entry, node, rootNode, instanceLocation, state.isValidationEnabled(), errors, this.validationContext.getConfig().getPropertyWalkListenerRunner(), true);
+ walkSchema(executionContext, entry, node, rootNode, instanceLocation, state.isValidationEnabled(), errors, this.validationContext.getConfig().getPropertyWalkListenerRunner());
}
// check whether the node which has not matched was mandatory or not
@@ -165,11 +166,12 @@ public class PropertiesValidator extends BaseJsonValidator { applyPropertyDefaults((ObjectNode) node);
}
if (shouldValidateSchema) {
- validationMessages.union(validate(executionContext, node, rootNode, instanceLocation));
+ validationMessages.union(validate(executionContext, node == null ? MissingNode.getInstance() : node, rootNode,
+ instanceLocation));
} else {
WalkListenerRunner propertyWalkListenerRunner = this.validationContext.getConfig().getPropertyWalkListenerRunner();
for (Map.Entry<String, JsonSchema> entry : this.schemas.entrySet()) {
- walkSchema(executionContext, entry, node, rootNode, instanceLocation, shouldValidateSchema, validationMessages, propertyWalkListenerRunner, false);
+ walkSchema(executionContext, entry, node, rootNode, instanceLocation, shouldValidateSchema, validationMessages, propertyWalkListenerRunner);
}
}
return validationMessages;
@@ -215,7 +217,7 @@ public class PropertiesValidator extends BaseJsonValidator { private void walkSchema(ExecutionContext executionContext, Map.Entry<String, JsonSchema> entry, JsonNode node,
JsonNode rootNode, JsonNodePath instanceLocation, boolean shouldValidateSchema,
- SetView<ValidationMessage> validationMessages, WalkListenerRunner propertyWalkListenerRunner, boolean skipWalk) {
+ SetView<ValidationMessage> validationMessages, WalkListenerRunner propertyWalkListenerRunner) {
JsonSchema propertySchema = entry.getValue();
JsonNode propertyNode = (node == null ? null : node.get(entry.getKey()));
JsonNodePath path = instanceLocation.append(entry.getKey());
@@ -226,7 +228,7 @@ public class PropertiesValidator extends BaseJsonValidator { // Attempt to get the property node again in case the propertyNode was updated
propertyNode = node.get(entry.getKey());
}
- if (executeWalk && !(skipWalk && propertyNode == null)) {
+ if (executeWalk) {
validationMessages.union(
propertySchema.walk(executionContext, propertyNode, rootNode, path, shouldValidateSchema));
}
diff --git a/src/main/java/com/networknt/schema/RecursiveRefValidator.java b/src/main/java/com/networknt/schema/RecursiveRefValidator.java index e73bb7d..8cd0de7 100644 --- a/src/main/java/com/networknt/schema/RecursiveRefValidator.java +++ b/src/main/java/com/networknt/schema/RecursiveRefValidator.java @@ -110,6 +110,22 @@ public class RecursiveRefValidator extends BaseJsonValidator { .arguments(schemaNode.asText()).build();
throw new InvalidSchemaRefException(validationMessage);
}
+ if (node == null) {
+ // Check for circular dependency
+ SchemaLocation schemaLocation = refSchema.getSchemaLocation();
+ JsonSchema check = refSchema;
+ boolean circularDependency = false;
+ while (check.getEvaluationParentSchema() != null) {
+ check = check.getEvaluationParentSchema();
+ if (check.getSchemaLocation().equals(schemaLocation)) {
+ circularDependency = true;
+ break;
+ }
+ }
+ if (circularDependency) {
+ return Collections.emptySet();
+ }
+ }
return refSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
}
diff --git a/src/main/java/com/networknt/schema/RefValidator.java b/src/main/java/com/networknt/schema/RefValidator.java index 1a32e6e..e6b803e 100644 --- a/src/main/java/com/networknt/schema/RefValidator.java +++ b/src/main/java/com/networknt/schema/RefValidator.java @@ -195,6 +195,22 @@ public class RefValidator extends BaseJsonValidator { .arguments(schemaNode.asText()).build();
throw new InvalidSchemaRefException(validationMessage);
}
+ if (node == null) {
+ // Check for circular dependency
+ SchemaLocation schemaLocation = refSchema.getSchemaLocation();
+ JsonSchema check = refSchema;
+ boolean circularDependency = false;
+ while (check.getEvaluationParentSchema() != null) {
+ check = check.getEvaluationParentSchema();
+ if (check.getSchemaLocation().equals(schemaLocation)) {
+ circularDependency = true;
+ break;
+ }
+ }
+ if (circularDependency) {
+ return Collections.emptySet();
+ }
+ }
return refSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
}
diff --git a/src/test/java/com/networknt/schema/ItemsValidator202012Test.java b/src/test/java/com/networknt/schema/ItemsValidator202012Test.java index 401e182..f7330f3 100644 --- a/src/test/java/com/networknt/schema/ItemsValidator202012Test.java +++ b/src/test/java/com/networknt/schema/ItemsValidator202012Test.java @@ -18,12 +18,18 @@ package com.networknt.schema; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import com.networknt.schema.SpecVersion.VersionFlag;
+import com.networknt.schema.walk.JsonSchemaWalkListener;
+import com.networknt.schema.walk.WalkEvent;
+import com.networknt.schema.walk.WalkFlow;
/**
* ItemsValidatorTest.
@@ -55,4 +61,86 @@ public class ItemsValidator202012Test { assertEquals("/1: string found, integer expected", message.getMessage());
assertNull(message.getProperty());
}
+
+ @Test
+ void walkNull() {
+ String schemaData = "{\r\n"
+ + " \"items\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " }\r\n"
+ + "}";
+ JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012);
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setPathType(PathType.JSON_POINTER);
+ config.addItemWalkListener(new JsonSchemaWalkListener() {
+
+ @Override
+ public WalkFlow onWalkStart(WalkEvent walkEvent) {
+ return WalkFlow.CONTINUE;
+ }
+
+ @Override
+ public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext()
+ .getCollectorContext()
+ .getCollectorMap()
+ .computeIfAbsent("items", key -> new ArrayList<JsonNodePath>());
+ items.add(walkEvent);
+ }
+ });
+ JsonSchema schema = factory.getSchema(schemaData, config);
+ ValidationResult result = schema.walk(null, true);
+ assertTrue(result.getValidationMessages().isEmpty());
+
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
+ assertEquals(1, items.size());
+ assertEquals("/0", items.get(0).getInstanceLocation().toString());
+ }
+
+ @Test
+ void walkNullPrefixItems() {
+ String schemaData = "{\r\n"
+ + " \"prefixItems\": [\r\n"
+ + " {\r\n"
+ + " \"type\": \"integer\"\r\n"
+ + " }\r\n"
+ + " ],\r\n"
+ + " \"items\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " }\r\n"
+ + "}";
+ JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012);
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setPathType(PathType.JSON_POINTER);
+ config.addItemWalkListener(new JsonSchemaWalkListener() {
+
+ @Override
+ public WalkFlow onWalkStart(WalkEvent walkEvent) {
+ return WalkFlow.CONTINUE;
+ }
+
+ @Override
+ public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext()
+ .getCollectorContext()
+ .getCollectorMap()
+ .computeIfAbsent("items", key -> new ArrayList<JsonNodePath>());
+ items.add(walkEvent);
+ }
+ });
+ JsonSchema schema = factory.getSchema(schemaData, config);
+ ValidationResult result = schema.walk(null, true);
+ assertTrue(result.getValidationMessages().isEmpty());
+
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
+ assertEquals(2, items.size());
+ assertEquals("/0", items.get(0).getInstanceLocation().toString());
+ assertEquals("prefixItems", items.get(0).getKeyword());
+ assertEquals("/1", items.get(1).getInstanceLocation().toString());
+ assertEquals("items", items.get(1).getKeyword());
+ }
}
diff --git a/src/test/java/com/networknt/schema/ItemsValidatorTest.java b/src/test/java/com/networknt/schema/ItemsValidatorTest.java index 8823028..901dbc0 100644 --- a/src/test/java/com/networknt/schema/ItemsValidatorTest.java +++ b/src/test/java/com/networknt/schema/ItemsValidatorTest.java @@ -18,12 +18,22 @@ package com.networknt.schema; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.SpecVersion.VersionFlag;
+import com.networknt.schema.serialization.JsonMapperFactory;
+import com.networknt.schema.walk.JsonSchemaWalkListener;
+import com.networknt.schema.walk.WalkEvent;
+import com.networknt.schema.walk.WalkFlow;
/**
* ItemsValidatorTest.
@@ -111,4 +121,248 @@ public class ItemsValidatorTest { assertEquals(": index '1' is not defined in the schema and the schema does not allow additional items", message.getMessage());
assertNull(message.getProperty());
}
+
+ @Test
+ void walk() {
+ String schemaData = "{\r\n"
+ + " \"items\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " }\r\n"
+ + "}";
+ JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909);
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setPathType(PathType.JSON_POINTER);
+ config.addItemWalkListener(new JsonSchemaWalkListener() {
+
+ @Override
+ public WalkFlow onWalkStart(WalkEvent walkEvent) {
+ return WalkFlow.CONTINUE;
+ }
+
+ @Override
+ public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext()
+ .getCollectorContext()
+ .getCollectorMap()
+ .computeIfAbsent("items", key -> new ArrayList<JsonNodePath>());
+ items.add(walkEvent);
+ }
+ });
+ JsonSchema schema = factory.getSchema(schemaData, config);
+ ValidationResult result = schema.walk("[\"the\",\"quick\",\"brown\"]", InputFormat.JSON, true);
+ assertTrue(result.getValidationMessages().isEmpty());
+
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
+ assertEquals(3, items.size());
+ assertEquals("/0", items.get(0).getInstanceLocation().toString());
+ assertEquals("/1", items.get(1).getInstanceLocation().toString());
+ assertEquals("/2", items.get(2).getInstanceLocation().toString());
+ }
+
+ @Test
+ void walkNull() {
+ String schemaData = "{\r\n"
+ + " \"items\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " }\r\n"
+ + "}";
+ JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909);
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setPathType(PathType.JSON_POINTER);
+ config.addItemWalkListener(new JsonSchemaWalkListener() {
+
+ @Override
+ public WalkFlow onWalkStart(WalkEvent walkEvent) {
+ return WalkFlow.CONTINUE;
+ }
+
+ @Override
+ public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext()
+ .getCollectorContext()
+ .getCollectorMap()
+ .computeIfAbsent("items", key -> new ArrayList<JsonNodePath>());
+ items.add(walkEvent);
+ }
+ });
+ JsonSchema schema = factory.getSchema(schemaData, config);
+ ValidationResult result = schema.walk(null, true);
+ assertTrue(result.getValidationMessages().isEmpty());
+
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
+ assertEquals(1, items.size());
+ assertEquals("/0", items.get(0).getInstanceLocation().toString());
+ }
+
+ @Test
+ void walkNullTupleItemsAdditional() {
+ String schemaData = "{\r\n"
+ + " \"items\": [\r\n"
+ + " {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " }\r\n,"
+ + " {\r\n"
+ + " \"type\": \"integer\"\r\n"
+ + " }\r\n"
+ + " ],\r\n"
+ + " \"additionalItems\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " }\r\n"
+ + "}";
+ JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909);
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setPathType(PathType.JSON_POINTER);
+ config.addItemWalkListener(new JsonSchemaWalkListener() {
+
+ @Override
+ public WalkFlow onWalkStart(WalkEvent walkEvent) {
+ return WalkFlow.CONTINUE;
+ }
+
+ @Override
+ public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext()
+ .getCollectorContext()
+ .getCollectorMap()
+ .computeIfAbsent("items", key -> new ArrayList<JsonNodePath>());
+ items.add(walkEvent);
+ }
+ });
+ JsonSchema schema = factory.getSchema(schemaData, config);
+ ValidationResult result = schema.walk(null, true);
+ assertTrue(result.getValidationMessages().isEmpty());
+
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
+ assertEquals(3, items.size());
+ assertEquals("/0", items.get(0).getInstanceLocation().toString());
+ assertEquals("items", items.get(0).getKeyword());
+ assertNull(items.get(0).getInstanceNode());
+ assertEquals("/1", items.get(1).getInstanceLocation().toString());
+ assertEquals("items", items.get(1).getKeyword());
+ assertNull(items.get(1).getInstanceNode());
+ assertEquals("/2", items.get(2).getInstanceLocation().toString());
+ assertEquals("additionalItems", items.get(2).getKeyword());
+ assertNull(items.get(2).getInstanceNode());
+ }
+
+ @Test
+ void walkTupleItemsAdditional() throws JsonMappingException, JsonProcessingException {
+ String schemaData = "{\r\n"
+ + " \"items\": [\r\n"
+ + " {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " }\r\n,"
+ + " {\r\n"
+ + " \"type\": \"integer\"\r\n"
+ + " }\r\n"
+ + " ],\r\n"
+ + " \"additionalItems\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " }\r\n"
+ + "}";
+ JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909);
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setPathType(PathType.JSON_POINTER);
+ config.addItemWalkListener(new JsonSchemaWalkListener() {
+
+ @Override
+ public WalkFlow onWalkStart(WalkEvent walkEvent) {
+ return WalkFlow.CONTINUE;
+ }
+
+ @Override
+ public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext()
+ .getCollectorContext()
+ .getCollectorMap()
+ .computeIfAbsent("items", key -> new ArrayList<JsonNodePath>());
+ items.add(walkEvent);
+ }
+ });
+ JsonSchema schema = factory.getSchema(schemaData, config);
+ JsonNode input = JsonMapperFactory.getInstance().readTree("[\"hello\"]");
+ ValidationResult result = schema.walk(input, true);
+ assertTrue(result.getValidationMessages().isEmpty());
+
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
+ assertEquals(3, items.size());
+ assertEquals("/0", items.get(0).getInstanceLocation().toString());
+ assertEquals("items", items.get(0).getKeyword());
+ assertEquals("hello", items.get(0).getInstanceNode().textValue());
+ assertEquals("/1", items.get(1).getInstanceLocation().toString());
+ assertEquals("items", items.get(1).getKeyword());
+ assertNull(items.get(1).getInstanceNode());
+ assertEquals("/2", items.get(2).getInstanceLocation().toString());
+ assertEquals("additionalItems", items.get(2).getKeyword());
+ assertNull(items.get(2).getInstanceNode());
+ }
+
+ @Test
+ void walkTupleItemsAdditionalDefaults() throws JsonMappingException, JsonProcessingException {
+ String schemaData = "{\r\n"
+ + " \"items\": [\r\n"
+ + " {\r\n"
+ + " \"type\": \"string\",\r\n"
+ + " \"default\": \"1\"\r\n"
+ + " },\r\n"
+ + " {\r\n"
+ + " \"type\": \"integer\",\r\n"
+ + " \"default\": 2\r\n"
+ + " }\r\n"
+ + " ],\r\n"
+ + " \"additionalItems\": {\r\n"
+ + " \"type\": \"string\",\r\n"
+ + " \"default\": \"additional\"\r\n"
+ + " }\r\n"
+ + "}";
+ JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909);
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setApplyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true));
+ config.setPathType(PathType.JSON_POINTER);
+ config.addItemWalkListener(new JsonSchemaWalkListener() {
+
+ @Override
+ public WalkFlow onWalkStart(WalkEvent walkEvent) {
+ return WalkFlow.CONTINUE;
+ }
+
+ @Override
+ public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext()
+ .getCollectorContext()
+ .getCollectorMap()
+ .computeIfAbsent("items", key -> new ArrayList<JsonNodePath>());
+ items.add(walkEvent);
+ }
+ });
+ JsonSchema schema = factory.getSchema(schemaData, config);
+ JsonNode input = JsonMapperFactory.getInstance().readTree("[null, null, null, null]");
+ ValidationResult result = schema.walk(input, true);
+ assertTrue(result.getValidationMessages().isEmpty());
+
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
+ assertEquals(4, items.size());
+ assertEquals("/0", items.get(0).getInstanceLocation().toString());
+ assertEquals("items", items.get(0).getKeyword());
+ assertEquals("1", items.get(0).getInstanceNode().textValue());
+ assertEquals("/1", items.get(1).getInstanceLocation().toString());
+ assertEquals("items", items.get(1).getKeyword());
+ assertEquals(2, items.get(1).getInstanceNode().intValue());
+ assertEquals("/2", items.get(2).getInstanceLocation().toString());
+ assertEquals("additionalItems", items.get(2).getKeyword());
+ assertEquals("additional", items.get(2).getInstanceNode().asText());
+ assertEquals("/3", items.get(3).getInstanceLocation().toString());
+ assertEquals("additionalItems", items.get(3).getKeyword());
+ assertEquals("additional", items.get(3).getInstanceNode().asText());
+ }
}
diff --git a/src/test/java/com/networknt/schema/PrefixItemsValidatorTest.java b/src/test/java/com/networknt/schema/PrefixItemsValidatorTest.java index b57d822..a06eda4 100644 --- a/src/test/java/com/networknt/schema/PrefixItemsValidatorTest.java +++ b/src/test/java/com/networknt/schema/PrefixItemsValidatorTest.java @@ -4,8 +4,17 @@ import org.junit.jupiter.api.DynamicContainer; import org.junit.jupiter.api.DynamicNode; import org.junit.jupiter.api.Test; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.SpecVersion.VersionFlag; +import com.networknt.schema.serialization.JsonMapperFactory; +import com.networknt.schema.walk.JsonSchemaWalkListener; +import com.networknt.schema.walk.WalkEvent; +import com.networknt.schema.walk.WalkFlow; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.stream.Stream; @@ -54,8 +63,8 @@ public class PrefixItemsValidatorTest extends AbstractJsonSchemaTestSuite { Set<ValidationMessage> messages = schema.validate(inputData, InputFormat.JSON); assertFalse(messages.isEmpty()); ValidationMessage message = messages.iterator().next(); - assertEquals("/prefixItems/type", message.getEvaluationPath().toString()); - assertEquals("https://www.example.org/schema#/prefixItems/type", message.getSchemaLocation().toString()); + assertEquals("/prefixItems/0/type", message.getEvaluationPath().toString()); + assertEquals("https://www.example.org/schema#/prefixItems/0/type", message.getSchemaLocation().toString()); assertEquals("/0", message.getInstanceLocation().toString()); assertEquals("\"string\"", message.getSchemaNode().toString()); assertEquals("1", message.getInstanceNode().toString()); @@ -109,4 +118,115 @@ public class PrefixItemsValidatorTest extends AbstractJsonSchemaTestSuite { assertEquals(": index '2' is not defined in the schema and the schema does not allow additional items", message.getMessage()); assertNull(message.getProperty()); } + + @Test + void walkNull() { + String schemaData = "{\n" + + " \"prefixItems\": [\n" + + " {\n" + + " \"type\": \"integer\"\n" + + " },\n" + + " {\n" + + " \"type\": \"string\"\n" + + " },\n" + + " {\n" + + " \"type\": \"integer\"\n" + + " }\n" + + " ]\n" + + "}"; + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); + SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + config.setPathType(PathType.JSON_POINTER); + config.addItemWalkListener(new JsonSchemaWalkListener() { + + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + return WalkFlow.CONTINUE; + } + + @Override + public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) { + @SuppressWarnings("unchecked") + List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("items", key -> new ArrayList<JsonNodePath>()); + items.add(walkEvent); + } + }); + JsonSchema schema = factory.getSchema(schemaData, config); + ValidationResult result = schema.walk(null, true); + assertTrue(result.getValidationMessages().isEmpty()); + + @SuppressWarnings("unchecked") + List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items"); + assertEquals(3, items.size()); + assertEquals("/0", items.get(0).getInstanceLocation().toString()); + assertEquals("prefixItems", items.get(0).getKeyword()); + assertEquals("#/prefixItems/0", items.get(0).getSchema().getSchemaLocation().toString()); + assertEquals("/1", items.get(1).getInstanceLocation().toString()); + assertEquals("prefixItems", items.get(1).getKeyword()); + assertEquals("#/prefixItems/1", items.get(1).getSchema().getSchemaLocation().toString()); + assertEquals("/2", items.get(2).getInstanceLocation().toString()); + assertEquals("prefixItems", items.get(2).getKeyword()); + assertEquals("#/prefixItems/2", items.get(2).getSchema().getSchemaLocation().toString()); + } + + @Test + void walkDefaults() throws JsonMappingException, JsonProcessingException { + String schemaData = "{\n" + + " \"prefixItems\": [\n" + + " {\n" + + " \"type\": \"integer\",\n" + + " \"default\": 1\n" + + " },\n" + + " {\n" + + " \"type\": \"string\",\n" + + " \"default\": \"2\"\n" + + " },\n" + + " {\n" + + " \"type\": \"integer\",\n" + + " \"default\": 3\n" + + " }\n" + + " ]\n" + + "}"; + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); + SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + config.setPathType(PathType.JSON_POINTER); + config.setApplyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)); + config.addItemWalkListener(new JsonSchemaWalkListener() { + + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + return WalkFlow.CONTINUE; + } + + @Override + public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) { + @SuppressWarnings("unchecked") + List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("items", key -> new ArrayList<JsonNodePath>()); + items.add(walkEvent); + } + }); + JsonSchema schema = factory.getSchema(schemaData, config); + JsonNode input = JsonMapperFactory.getInstance().readTree("[null, null]"); + ValidationResult result = schema.walk(input, true); + assertTrue(result.getValidationMessages().isEmpty()); + + @SuppressWarnings("unchecked") + List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items"); + assertEquals(3, items.size()); + assertEquals("/0", items.get(0).getInstanceLocation().toString()); + assertEquals("prefixItems", items.get(0).getKeyword()); + assertEquals("#/prefixItems/0", items.get(0).getSchema().getSchemaLocation().toString()); + assertEquals("/1", items.get(1).getInstanceLocation().toString()); + assertEquals("prefixItems", items.get(1).getKeyword()); + assertEquals("#/prefixItems/1", items.get(1).getSchema().getSchemaLocation().toString()); + assertEquals("/2", items.get(2).getInstanceLocation().toString()); + assertEquals("prefixItems", items.get(2).getKeyword()); + assertEquals("#/prefixItems/2", items.get(2).getSchema().getSchemaLocation().toString()); + } } diff --git a/src/test/java/com/networknt/schema/TypeValidatorTest.java b/src/test/java/com/networknt/schema/TypeValidatorTest.java index 8560b56..487b814 100644 --- a/src/test/java/com/networknt/schema/TypeValidatorTest.java +++ b/src/test/java/com/networknt/schema/TypeValidatorTest.java @@ -16,6 +16,7 @@ package com.networknt.schema;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Set;
@@ -139,4 +140,14 @@ public class TypeValidatorTest { messages = schema.validate("2.000001", InputFormat.JSON);
assertEquals(1, messages.size());
}
+
+ @Test
+ void walkNull() {
+ String schemaData = "{\r\n"
+ + " \"type\": \"integer\"\r\n"
+ + "}";
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V4).getSchema(schemaData);
+ ValidationResult result = schema.walk(null, true);
+ assertTrue(result.getValidationMessages().isEmpty());
+ }
}
diff --git a/src/test/java/com/networknt/schema/walk/JsonSchemaWalkListenerTest.java b/src/test/java/com/networknt/schema/walk/JsonSchemaWalkListenerTest.java index d4b6701..af19ac0 100644 --- a/src/test/java/com/networknt/schema/walk/JsonSchemaWalkListenerTest.java +++ b/src/test/java/com/networknt/schema/walk/JsonSchemaWalkListenerTest.java @@ -38,6 +38,7 @@ import com.networknt.schema.ApplyDefaultsStrategy; import com.networknt.schema.InputFormat;
import com.networknt.schema.ItemsValidator;
import com.networknt.schema.ItemsValidator202012;
+import com.networknt.schema.JsonNodePath;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.JsonSchemaRef;
@@ -811,4 +812,129 @@ class JsonSchemaWalkListenerTest { assertEquals("{\"name\":\"John Doe\",\"email\":\"john.doe@gmail.com\"}", inputNode.toString());
assertTrue(result.getValidationMessages().isEmpty());
}
+
+ /**
+ * Issue 989
+ */
+ @Test
+ void itemListenerDraft201909() {
+ String schemaData = " {\r\n"
+ + " \"type\": \"object\",\r\n"
+ + " \"properties\": {\r\n"
+ + " \"name\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " },\r\n"
+ + " \"children\": {\r\n"
+ + " \"type\": \"array\",\r\n"
+ + " \"items\": {\r\n"
+ + " \"type\": \"object\",\r\n"
+ + " \"properties\": {\r\n"
+ + " \"name\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " }";
+ JsonSchemaWalkListener listener = new JsonSchemaWalkListener() {
+ @Override
+ public WalkFlow onWalkStart(WalkEvent walkEvent) {
+ return WalkFlow.CONTINUE;
+ }
+
+ @Override
+ public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext()
+ .getCollectorContext()
+ .getCollectorMap()
+ .computeIfAbsent("items", key -> new ArrayList<JsonNodePath>());
+ items.add(walkEvent);
+ }
+ };
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.addItemWalkListener(listener);
+ config.addPropertyWalkListener(listener);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V201909).getSchema(schemaData, config);
+ ValidationResult result = schema.walk(null, true);
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
+ assertEquals(4, items.size());
+ assertEquals("$.name", items.get(0).getInstanceLocation().toString());
+ assertEquals("properties", items.get(0).getKeyword());
+ assertEquals("#/properties/name", items.get(0).getSchema().getSchemaLocation().toString());
+ assertEquals("$.children[0].name", items.get(1).getInstanceLocation().toString());
+ assertEquals("properties", items.get(1).getKeyword());
+ assertEquals("#/properties/children/items/properties/name", items.get(1).getSchema().getSchemaLocation().toString());
+ assertEquals("$.children[0]", items.get(2).getInstanceLocation().toString());
+ assertEquals("items", items.get(2).getKeyword());
+ assertEquals("#/properties/children/items", items.get(2).getSchema().getSchemaLocation().toString());
+ assertEquals("$.children", items.get(3).getInstanceLocation().toString());
+ assertEquals("properties", items.get(3).getKeyword());
+ assertEquals("#/properties/children", items.get(3).getSchema().getSchemaLocation().toString());
+ }
+
+ /**
+ * Issue 989
+ */
+ @Test
+ void itemListenerDraft202012() {
+ String schemaData = " {\r\n"
+ + " \"type\": \"object\",\r\n"
+ + " \"properties\": {\r\n"
+ + " \"name\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " },\r\n"
+ + " \"children\": {\r\n"
+ + " \"type\": \"array\",\r\n"
+ + " \"items\": {\r\n"
+ + " \"type\": \"object\",\r\n"
+ + " \"properties\": {\r\n"
+ + " \"name\": {\r\n"
+ + " \"type\": \"string\"\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " }";
+ JsonSchemaWalkListener listener = new JsonSchemaWalkListener() {
+ @Override
+ public WalkFlow onWalkStart(WalkEvent walkEvent) {
+ return WalkFlow.CONTINUE;
+ }
+
+ @Override
+ public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext()
+ .getCollectorContext()
+ .getCollectorMap()
+ .computeIfAbsent("items", key -> new ArrayList<JsonNodePath>());
+ items.add(walkEvent);
+ }
+ };
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.addItemWalkListener(listener);
+ config.addPropertyWalkListener(listener);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ ValidationResult result = schema.walk(null, true);
+ @SuppressWarnings("unchecked")
+ List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
+ assertEquals(4, items.size());
+ assertEquals("$.name", items.get(0).getInstanceLocation().toString());
+ assertEquals("properties", items.get(0).getKeyword());
+ assertEquals("#/properties/name", items.get(0).getSchema().getSchemaLocation().toString());
+ assertEquals("$.children[0].name", items.get(1).getInstanceLocation().toString());
+ assertEquals("properties", items.get(1).getKeyword());
+ assertEquals("#/properties/children/items/properties/name", items.get(1).getSchema().getSchemaLocation().toString());
+ assertEquals("$.children[0]", items.get(2).getInstanceLocation().toString());
+ assertEquals("items", items.get(2).getKeyword());
+ assertEquals("#/properties/children/items", items.get(2).getSchema().getSchemaLocation().toString());
+ assertEquals("$.children", items.get(3).getInstanceLocation().toString());
+ assertEquals("properties", items.get(3).getKeyword());
+ assertEquals("#/properties/children", items.get(3).getSchema().getSchemaLocation().toString());
+ }
+
}
|