diff --git a/src/main/java/com/networknt/schema/AbsoluteIri.java b/src/main/java/com/networknt/schema/AbsoluteIri.java
index 1ade74363..890f5e003 100644
--- a/src/main/java/com/networknt/schema/AbsoluteIri.java
+++ b/src/main/java/com/networknt/schema/AbsoluteIri.java
@@ -17,6 +17,8 @@
import java.util.Objects;
+import com.networknt.schema.utils.Strings;
+
/**
* The absolute IRI is an IRI without the fragment.
*
@@ -131,7 +133,7 @@ public static String resolve(String parent, String iri) {
}
base = parent(base, scheme);
- String[] iriParts = iri.split("/");
+ String[] iriParts = Strings.split(iri, '/');
for (int x = 0; x < iriParts.length; x++) {
if ("..".equals(iriParts[x])) {
base = parent(base, scheme);
@@ -149,9 +151,6 @@ public static String resolve(String parent, String iri) {
}
}
}
- if (iri.endsWith("/")) {
- base = base + "/";
- }
return base;
}
}
diff --git a/src/main/java/com/networknt/schema/ExecutionContext.java b/src/main/java/com/networknt/schema/ExecutionContext.java
index 5cb78dba1..51037797f 100644
--- a/src/main/java/com/networknt/schema/ExecutionContext.java
+++ b/src/main/java/com/networknt/schema/ExecutionContext.java
@@ -16,6 +16,7 @@
package com.networknt.schema;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -25,7 +26,7 @@
import com.networknt.schema.annotation.Annotations;
import com.networknt.schema.keyword.DiscriminatorState;
import com.networknt.schema.path.NodePath;
-import com.networknt.schema.result.InstanceResults;
+//import com.networknt.schema.result.InstanceResults;
import com.networknt.schema.walk.WalkConfig;
/**
@@ -37,11 +38,40 @@ public class ExecutionContext {
private CollectorContext collectorContext = null;
private Annotations annotations = null;
- private InstanceResults instanceResults = null;
+// private InstanceResults instanceResults = null;
private List errors = new ArrayList<>();
- private Map discriminatorMapping = new HashMap<>();
+ private final Map discriminatorMapping = new HashMap<>();
+ NodePath evaluationPath;
+ final ArrayDeque evaluationSchema = new ArrayDeque<>(64);
+ final ArrayDeque evaluationSchemaPath = new ArrayDeque<>(64);
+
+ public NodePath getEvaluationPath() {
+ return evaluationPath;
+ }
+
+ public void evaluationPathAddLast(String token) {
+ this.evaluationPath = evaluationPath.append(token);
+ }
+
+ public void evaluationPathAddLast(int token) {
+ this.evaluationPath = evaluationPath.append(token);
+ }
+
+ public void evaluationPathRemoveLast() {
+ this.evaluationPath = evaluationPath.getParent();
+ }
+
+
+ public ArrayDeque getEvaluationSchema() {
+ return evaluationSchema;
+ }
+
+ public ArrayDeque getEvaluationSchemaPath() {
+ return evaluationSchemaPath;
+ }
+
public Map getDiscriminatorMapping() {
return discriminatorMapping;
}
@@ -156,12 +186,12 @@ public Annotations getAnnotations() {
return annotations;
}
- public InstanceResults getInstanceResults() {
- if (this.instanceResults == null) {
- this.instanceResults = new InstanceResults();
- }
- return instanceResults;
- }
+// public InstanceResults getInstanceResults() {
+// if (this.instanceResults == null) {
+// this.instanceResults = new InstanceResults();
+// }
+// return instanceResults;
+// }
/**
* Determines if the validator should immediately throw a fail fast exception if
@@ -224,4 +254,24 @@ public void walkConfig(Consumer customizer) {
customizer.accept(builder);
this.walkConfig = builder.build();
}
+
+ boolean unevaluatedPropertiesPresent = false;
+
+ boolean unevaluatedItemsPresent = false;
+
+ public boolean isUnevaluatedPropertiesPresent() {
+ return this.unevaluatedPropertiesPresent;
+ }
+
+ public boolean isUnevaluatedItemsPresent() {
+ return this.unevaluatedItemsPresent;
+ }
+
+ public void setUnevaluatedPropertiesPresent(boolean set) {
+ this.unevaluatedPropertiesPresent = set;
+ }
+
+ public void setUnevaluatedItemsPresent(boolean set) {
+ this.unevaluatedItemsPresent = set;
+ }
}
diff --git a/src/main/java/com/networknt/schema/Schema.java b/src/main/java/com/networknt/schema/Schema.java
index b11dd60ee..bfd264019 100644
--- a/src/main/java/com/networknt/schema/Schema.java
+++ b/src/main/java/com/networknt/schema/Schema.java
@@ -40,6 +40,7 @@
import com.networknt.schema.resource.ClasspathResourceLoader;
import com.networknt.schema.resource.InputStreamSource;
import com.networknt.schema.resource.ResourceLoader;
+import com.networknt.schema.annotation.Annotation;
import com.networknt.schema.keyword.KeywordType;
import com.networknt.schema.utils.JsonNodes;
@@ -63,9 +64,11 @@ public class Schema implements Validator {
* The validators sorted and indexed by evaluation path.
*/
private List validators = null;
+ private boolean unevaluatedPropertiesPresent = false;
+ private boolean unevaluatedItemsPresent = false;
+
private boolean validatorsLoaded = false;
private boolean recursiveAnchor = false;
- private TypeValidator typeValidator = null;
protected final JsonNode schemaNode;
protected final Schema parentSchema;
@@ -73,9 +76,6 @@ public class Schema implements Validator {
protected final SchemaContext schemaContext;
protected final boolean suppressSubSchemaRetrieval;
- protected final NodePath evaluationPath;
- protected final Schema evaluationParentSchema;
-
public JsonNode getSchemaNode() {
return this.schemaNode;
}
@@ -92,17 +92,6 @@ public boolean isSuppressSubSchemaRetrieval() {
return suppressSubSchemaRetrieval;
}
- public NodePath getEvaluationPath() {
- return evaluationPath;
- }
-
- public Schema getEvaluationParentSchema() {
- if (this.evaluationParentSchema != null) {
- return this.evaluationParentSchema;
- }
- return getParentSchema();
- }
-
protected Schema fetchSubSchemaNode(SchemaContext schemaContext) {
return this.suppressSubSchemaRetrieval ? null : obtainSubSchemaNode(this.schemaNode, schemaContext);
}
@@ -147,6 +136,14 @@ public static NodePath getInstance() {
}
public void validate(ExecutionContext executionContext, JsonNode node) {
+ /* Previously the evaluation path started with the fragment of the schema due to the way it was implemented
+ * as part of the schema's state
+ * int count = this.schemaLocation.getFragment().getNameCount();
+ * for (int x = 0; x < count; x++) {
+ * executionContext.evaluationPath.addLast(this.schemaLocation.getFragment().getElement(x));
+ * }
+ */
+ executionContext.evaluationPath = atRoot();
validate(executionContext, node, node, atRoot());
}
@@ -166,8 +163,8 @@ protected NodePath atRoot() {
return new NodePath(this.schemaContext.getSchemaRegistryConfig().getPathType());
}
- static Schema from(SchemaContext schemaContext, SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parent, boolean suppressSubSchemaRetrieval) {
- return new Schema(schemaContext, schemaLocation, evaluationPath, schemaNode, parent, suppressSubSchemaRetrieval);
+ static Schema from(SchemaContext schemaContext, SchemaLocation schemaLocation, JsonNode schemaNode, Schema parent, boolean suppressSubSchemaRetrieval) {
+ return new Schema(schemaContext, schemaLocation, schemaNode, parent, suppressSubSchemaRetrieval);
}
private boolean hasNoFragment(SchemaLocation schemaLocation) {
@@ -209,19 +206,13 @@ private static SchemaLocation resolve(SchemaLocation schemaLocation, JsonNode sc
}
}
- private Schema(SchemaContext schemaContext, SchemaLocation schemaLocation, NodePath evaluationPath,
+ private Schema(SchemaContext schemaContext, SchemaLocation schemaLocation,
JsonNode schemaNode, Schema parent, boolean suppressSubSchemaRetrieval) {
- /*
- super(resolve(schemaLocation, schemaNode, parent == null, schemaContext), evaluationPath, schemaNode, parent,
- null, null, schemaContext, suppressSubSchemaRetrieval);
- */
this.schemaContext = schemaContext;
this.schemaLocation = resolve(schemaLocation, schemaNode, parent == null, schemaContext);
this.schemaNode = schemaNode;
this.parentSchema = parent;
this.suppressSubSchemaRetrieval = suppressSubSchemaRetrieval;
- this.evaluationPath = evaluationPath;
- this.evaluationParentSchema = null;
String id = this.schemaContext.resolveSchemaId(this.schemaNode);
if (id != null) {
@@ -271,39 +262,29 @@ private Schema(SchemaContext schemaContext, SchemaLocation schemaLocation, NodeP
* @param validators the validators
* @param validatorsLoaded whether the validators are preloaded
* @param recursiveAnchor whether this is has a recursive anchor
- * @param typeValidator the type validator
* @param id the id
* @param suppressSubSchemaRetrieval to suppress sub schema retrieval
* @param schemaNode the schema node
* @param schemaContext the schema context
* @param parentSchema the parent schema
* @param schemaLocation the schema location
- * @param evaluationPath the evaluation path
- * @param evaluationParentSchema the evaluation parent schema
* @param errorMessage the error message
*/
protected Schema(
- /* Below from JsonSchema */
List validators,
boolean validatorsLoaded,
boolean recursiveAnchor,
TypeValidator typeValidator,
String id,
- /* Below from BaseJsonValidator */
boolean suppressSubSchemaRetrieval,
JsonNode schemaNode,
SchemaContext schemaContext,
Schema parentSchema,
SchemaLocation schemaLocation,
- NodePath evaluationPath,
- Schema evaluationParentSchema,
Map errorMessage) {
-// super(suppressSubSchemaRetrieval, schemaNode, schemaContext, errorMessageType, keyword,
-// parentSchema, schemaLocation, evaluationPath, evaluationParentSchema, errorMessage);
this.validators = validators;
this.validatorsLoaded = validatorsLoaded;
this.recursiveAnchor = recursiveAnchor;
- this.typeValidator = typeValidator;
this.id = id;
this.schemaContext = schemaContext;
@@ -311,90 +292,15 @@ protected Schema(
this.schemaNode = schemaNode;
this.parentSchema = parentSchema;
this.suppressSubSchemaRetrieval = suppressSubSchemaRetrieval;
- this.evaluationPath = evaluationPath;
- this.evaluationParentSchema = evaluationParentSchema;
}
- /**
- * Creates a schema using the current one as a template with the parent as the
- * ref.
- *
- * This is typically used if this schema is a schema resource that can be
- * pointed to by various references.
- *
- * @param refEvaluationParentSchema the parent ref
- * @param refEvaluationPath the ref evaluation path
- * @return the schema
- */
- public Schema fromRef(Schema refEvaluationParentSchema, NodePath refEvaluationPath) {
- SchemaContext schemaContext = new SchemaContext(this.getSchemaContext().getDialect(),
- this.getSchemaContext().getSchemaRegistry(),
- refEvaluationParentSchema.getSchemaContext().getSchemaReferences(),
- refEvaluationParentSchema.getSchemaContext().getSchemaResources(),
- refEvaluationParentSchema.getSchemaContext().getDynamicAnchors());
- NodePath evaluationPath = refEvaluationPath;
- Schema evaluationParentSchema = refEvaluationParentSchema;
- // Validator state is reset due to the changes in evaluation path
- boolean validatorsLoaded = false;
- TypeValidator typeValidator = null;
- List validators = null;
-
- return new Schema(
- /* Below from JsonSchema */
- validators,
- validatorsLoaded,
- recursiveAnchor,
- typeValidator,
- id,
- /* Below from BaseJsonValidator */
- suppressSubSchemaRetrieval,
- schemaNode,
- schemaContext,
- parentSchema, schemaLocation, evaluationPath,
- evaluationParentSchema, /* errorMessage */ null);
- }
-
-// public Schema withConfig(SchemaValidatorsConfig config) {
-// if (this.getValidationContext().getMetaSchema().getKeywords().containsKey("discriminator")
-// && !config.isDiscriminatorKeywordEnabled()) {
-// config = SchemaValidatorsConfig.builder(config)
-// .discriminatorKeywordEnabled(true)
-// .nullableKeywordEnabled(true)
-// .build();
-// }
-// if (!this.getValidationContext().getSchemaRegistryConfig().equals(config)) {
-// ValidationContext schemaContext = new ValidationContext(this.getValidationContext().getMetaSchema(),
-// this.getValidationContext().getSchemaRegistry(), config,
-// this.getValidationContext().getSchemaReferences(),
-// this.getValidationContext().getSchemaResources(),
-// this.getValidationContext().getDynamicAnchors());
-// boolean validatorsLoaded = false;
-// TypeValidator typeValidator = null;
-// List validators = null;
-// return new Schema(
-// /* Below from JsonSchema */
-// validators,
-// validatorsLoaded,
-// recursiveAnchor,
-// typeValidator,
-// id,
-// /* Below from BaseJsonValidator */
-// suppressSubSchemaRetrieval,
-// schemaNode,
-// schemaContext,
-// parentSchema,
-// schemaLocation,
-// evaluationPath,
-// evaluationParentSchema,
-// /* errorMessage */ null);
-//
-// }
-// return this;
-// }
-
public SchemaContext getSchemaContext() {
return this.schemaContext;
}
+
+ public boolean hasKeyword(String keyword) {
+ return this.schemaNode.has(keyword);
+ }
/**
* Find the schema node for $ref attribute.
@@ -461,7 +367,6 @@ public Schema getSubSchema(NodePath fragment) {
Schema subSchema = null;
JsonNode parentNode = parent.getSchemaNode();
SchemaLocation schemaLocation = document.getSchemaLocation();
- NodePath evaluationPath = document.getEvaluationPath();
int nameCount = fragment.getNameCount();
for (int x = 0; x < nameCount; x++) {
/*
@@ -478,10 +383,8 @@ public Schema getSubSchema(NodePath fragment) {
if (segment instanceof Number && parentNode.isArray()) {
int index = ((Number) segment).intValue();
schemaLocation = schemaLocation.append(index);
- evaluationPath = evaluationPath.append(index);
} else {
schemaLocation = schemaLocation.append(segment.toString());
- evaluationPath = evaluationPath.append(segment.toString());
}
/*
* The parent schema context is used to create as there can be changes in
@@ -492,7 +395,7 @@ public Schema getSubSchema(NodePath fragment) {
// if (!("definitions".equals(segment.toString()) || "$defs".equals(segment.toString())
// )) {
if (id != null || x == nameCount - 1) {
- subSchema = parent.getSchemaContext().newSchema(schemaLocation, evaluationPath, subSchemaNode,
+ subSchema = parent.getSchemaContext().newSchema(schemaLocation, subSchemaNode,
parent);
parent = subSchema;
schemaLocation = subSchema.getSchemaLocation();
@@ -516,7 +419,7 @@ public Schema getSubSchema(NodePath fragment) {
.message("Reference {0} cannot be resolved")
.instanceLocation(schemaLocation.getFragment())
.schemaLocation(schemaLocation)
- .evaluationPath(evaluationPath)
+ .evaluationPath(null)
.arguments(fragment).build();
throw new InvalidSchemaRefException(error);
}
@@ -605,14 +508,12 @@ private List read(JsonNode schemaNode) {
if (schemaNode.isBoolean()) {
validators = new ArrayList<>(1);
if (schemaNode.booleanValue()) {
- NodePath path = getEvaluationPath().append("true");
- KeywordValidator validator = this.schemaContext.newValidator(getSchemaLocation().append("true"), path,
+ KeywordValidator validator = this.schemaContext.newValidator(getSchemaLocation().append("true"),
"true", schemaNode, this);
validators.add(validator);
} else {
- NodePath path = getEvaluationPath().append("false");
KeywordValidator validator = this.schemaContext.newValidator(getSchemaLocation().append("false"),
- path, "false", schemaNode, this);
+ "false", schemaNode, this);
validators.add(validator);
}
} else {
@@ -625,7 +526,6 @@ private List read(JsonNode schemaNode) {
String pname = entry.getKey();
JsonNode nodeToUse = entry.getValue();
- NodePath path = getEvaluationPath().append(pname);
SchemaLocation schemaPath = getSchemaLocation().append(pname);
if ("$recursiveAnchor".equals(pname)) {
@@ -634,8 +534,8 @@ private List read(JsonNode schemaNode) {
.messageKey("internal.invalidRecursiveAnchor")
.message(
"The value of a $recursiveAnchor must be a Boolean literal but is {0}")
- .instanceLocation(path)
- .evaluationPath(path)
+ .instanceLocation(null)
+ .evaluationPath(null)
.schemaLocation(schemaPath)
.arguments(nodeToUse.getNodeType().toString())
.build();
@@ -644,17 +544,18 @@ private List read(JsonNode schemaNode) {
this.recursiveAnchor = nodeToUse.booleanValue();
}
- KeywordValidator validator = this.schemaContext.newValidator(schemaPath, path,
+ KeywordValidator validator = this.schemaContext.newValidator(schemaPath,
pname, nodeToUse, this);
if (validator != null) {
validators.add(validator);
+ if ("unevaluatedProperties".equals(pname)) {
+ this.unevaluatedPropertiesPresent = true;
+ } else if ("unevaluatedItems".equals(pname)) {
+ this.unevaluatedItemsPresent = true;
+ }
if ("$ref".equals(pname)) {
refValidator = validator;
- } else if ("type".equals(pname)) {
- if (validator instanceof TypeValidator) {
- this.typeValidator = (TypeValidator) validator;
- }
}
}
@@ -677,8 +578,8 @@ private List read(JsonNode schemaNode) {
* so that we can apply default values before validating required.
*/
private static final Comparator VALIDATOR_SORT = (lhs, rhs) -> {
- String lhsName = lhs.getEvaluationPath().getName(-1);
- String rhsName = rhs.getEvaluationPath().getName(-1);
+ String lhsName = lhs.getKeyword();
+ String rhsName = rhs.getKeyword();
if (lhsName.equals(rhsName)) return 0;
@@ -686,6 +587,9 @@ private List read(JsonNode schemaNode) {
if (lhsName.equals("discriminator")) return -1;
if (rhsName.equals("discriminator")) return 1;
+ if (lhsName.equals("type")) return -1;
+ if (rhsName.equals("type")) return 1;
+
if (lhsName.equals("properties")) return -1;
if (rhsName.equals("properties")) return 1;
if (lhsName.equals("patternProperties")) return -1;
@@ -702,14 +606,46 @@ private List read(JsonNode schemaNode) {
@Override
public void validate(ExecutionContext executionContext, JsonNode jsonNode, JsonNode rootNode, NodePath instanceLocation) {
- int currentErrors = executionContext.getErrors().size();
- for (KeywordValidator v : getValidators()) {
- v.validate(executionContext, jsonNode, rootNode, instanceLocation);
+ List validators = getValidators(); // Load the validators before checking the flags
+ executionContext.evaluationSchema.addLast(this);
+ boolean unevaluatedPropertiesPresent = executionContext.unevaluatedPropertiesPresent;
+ boolean unevaluatedItemsPresent = executionContext.unevaluatedItemsPresent;
+ if (this.unevaluatedPropertiesPresent) {
+ executionContext.unevaluatedPropertiesPresent = this.unevaluatedPropertiesPresent;
+ }
+ if (this.unevaluatedItemsPresent) {
+ executionContext.unevaluatedItemsPresent = this.unevaluatedItemsPresent;
}
- if (executionContext.getErrors().size() > currentErrors) {
- // Failed with assertion set result and drop all annotations from this schema
- // and all subschemas
- executionContext.getInstanceResults().setResult(instanceLocation, getSchemaLocation(), getEvaluationPath(), false);
+ try {
+ int currentErrors = executionContext.getErrors().size();
+ for (KeywordValidator v : validators) {
+ executionContext.evaluationPathAddLast(v.getKeyword());
+ executionContext.evaluationSchemaPath.addLast(v.getKeyword());
+ try {
+ v.validate(executionContext, jsonNode, rootNode, instanceLocation);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationSchemaPath.removeLast();
+ }
+ }
+ if (executionContext.getErrors().size() > currentErrors) {
+ // Failed with assertion set result and drop all annotations from this schema
+ // and all subschemas
+ List annotations = executionContext.getAnnotations().asMap().get(instanceLocation);
+ if (annotations != null) {
+ for (Annotation annotation : annotations) {
+ if (annotation.getEvaluationPath().startsWith(executionContext.getEvaluationPath())) {
+ annotation.setValid(false);
+ }
+ }
+ }
+
+ //executionContext.getInstanceResults().setResult(instanceLocation, getSchemaLocation(), executionContext.getEvaluationPath(), false);
+ }
+ } finally {
+ executionContext.evaluationSchema.removeLast();
+ executionContext.unevaluatedPropertiesPresent = unevaluatedPropertiesPresent;
+ executionContext.unevaluatedItemsPresent = unevaluatedItemsPresent;
}
}
@@ -1568,6 +1504,7 @@ private T walkAtNodeInternal(ExecutionContext executionContext, JsonNode nod
executionCustomizer.customize(executionContext, this.schemaContext);
}
// Walk through the schema.
+ executionContext.evaluationPath = atRoot();
walk(executionContext, node, rootNode, instanceLocation, validate);
return format.format(this, executionContext, this.schemaContext);
}
@@ -1576,44 +1513,53 @@ private T walkAtNodeInternal(ExecutionContext executionContext, JsonNode nod
public void walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode,
NodePath instanceLocation, boolean shouldValidateSchema) {
// Walk through all the JSONWalker's.
- int currentErrors = executionContext.getErrors().size();
- for (KeywordValidator validator : getValidators()) {
- NodePath evaluationPathWithKeyword = validator.getEvaluationPath();
- try {
- // Call all the pre-walk listeners. If at least one of the pre walk listeners
- // returns SKIP, then skip the walk.
- if (executionContext.getWalkConfig().getKeywordWalkListenerRunner().runPreWalkListeners(executionContext,
- evaluationPathWithKeyword.getName(-1), node, rootNode, instanceLocation,
- this, validator)) {
- validator.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ List validators = getValidators(); // Load the validators before checking the flags
+ executionContext.evaluationSchema.addLast(this);
+ boolean unevaluatedPropertiesPresent = executionContext.unevaluatedPropertiesPresent;
+ boolean unevaluatedItemsPresent = executionContext.unevaluatedItemsPresent;
+ if (this.unevaluatedPropertiesPresent) {
+ executionContext.unevaluatedPropertiesPresent = this.unevaluatedPropertiesPresent;
+ }
+ if (this.unevaluatedItemsPresent) {
+ executionContext.unevaluatedItemsPresent = this.unevaluatedItemsPresent;
+ }
+ try {
+ int currentErrors = executionContext.getErrors().size();
+ for (KeywordValidator validator : validators) {
+ try {
+ // Call all the pre-walk listeners. If at least one of the pre walk listeners
+ // returns SKIP, then skip the walk.
+ if (executionContext.getWalkConfig().getKeywordWalkListenerRunner().runPreWalkListeners(executionContext,
+ validator.getKeyword(), node, rootNode, instanceLocation,
+ this, validator)) {
+ executionContext.evaluationPathAddLast(validator.getKeyword());
+ executionContext.evaluationSchemaPath.addLast(validator.getKeyword());
+ try {
+ validator.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationSchemaPath.removeLast();
+ }
+ }
+ } finally {
+ // Call all the post-walk listeners.
+ executionContext.getWalkConfig().getKeywordWalkListenerRunner().runPostWalkListeners(executionContext,
+ validator.getKeyword(), node, rootNode, instanceLocation,
+ this, validator,
+ executionContext.getErrors().subList(currentErrors, executionContext.getErrors().size()));
}
- } finally {
- // Call all the post-walk listeners.
- executionContext.getWalkConfig().getKeywordWalkListenerRunner().runPostWalkListeners(executionContext,
- evaluationPathWithKeyword.getName(-1), node, rootNode, instanceLocation,
- this, validator,
- executionContext.getErrors().subList(currentErrors, executionContext.getErrors().size()));
}
+ } finally {
+ executionContext.evaluationSchema.removeLast();
+ executionContext.unevaluatedPropertiesPresent = unevaluatedPropertiesPresent;
+ executionContext.unevaluatedItemsPresent = unevaluatedItemsPresent;
}
}
/************************ END OF WALK METHODS **********************************/
@Override
public String toString() {
- return "\"" + getEvaluationPath() + "\" : " + getSchemaNode().toString();
- }
-
- public boolean hasTypeValidator() {
- return getTypeValidator() != null;
- }
-
- public TypeValidator getTypeValidator() {
- // As the validators are lazy loaded the typeValidator is only known if the
- // validators are not null
- if (this.validators == null) {
- getValidators();
- }
- return this.typeValidator;
+ return getSchemaNode().toString();
}
public List getValidators() {
@@ -1634,15 +1580,23 @@ public List getValidators() {
*/
public void initializeValidators() {
if (!this.validatorsLoaded) {
- for (final KeywordValidator validator : getValidators()) {
- validator.preloadSchema();
- }
/*
- * This is only set to true after the preload as it may throw an exception for
- * instance if the remote host is unavailable and we may want to be able to try
- * again.
+ * This is set to true here to prevent recursive cyclic loading of the validators
*/
this.validatorsLoaded = true;
+ try {
+ for (final KeywordValidator validator : getValidators()) {
+ validator.preloadSchema();
+ }
+ } catch (RuntimeException e) {
+ /*
+ * As the preload may throw an exception for
+ * instance if the remote host is unavailable and we may want to be able to try
+ * again.
+ */
+ this.validatorsLoaded = false;
+ throw e;
+ }
}
}
diff --git a/src/main/java/com/networknt/schema/SchemaContext.java b/src/main/java/com/networknt/schema/SchemaContext.java
index 7ff84923d..783ff91ac 100644
--- a/src/main/java/com/networknt/schema/SchemaContext.java
+++ b/src/main/java/com/networknt/schema/SchemaContext.java
@@ -22,7 +22,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.dialect.Dialect;
import com.networknt.schema.keyword.KeywordValidator;
-import com.networknt.schema.path.NodePath;
/**
* The schema context associated with a schema and all its validators.
@@ -118,13 +117,13 @@ public SchemaContext(Dialect dialect, SchemaRegistry schemaRegistry,
}
}
- public Schema newSchema(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema) {
- return getSchemaRegistry().create(this, schemaLocation, evaluationPath, schemaNode, parentSchema);
+ public Schema newSchema(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema) {
+ return getSchemaRegistry().create(this, schemaLocation, schemaNode, parentSchema);
}
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath,
+ public KeywordValidator newValidator(SchemaLocation schemaLocation,
String keyword /* keyword */, JsonNode schemaNode, Schema parentSchema) {
- return this.dialect.newValidator(this, schemaLocation, evaluationPath, keyword, schemaNode, parentSchema);
+ return this.dialect.newValidator(this, schemaLocation, keyword, schemaNode, parentSchema);
}
public String resolveSchemaId(JsonNode schemaNode) {
diff --git a/src/main/java/com/networknt/schema/SchemaLocation.java b/src/main/java/com/networknt/schema/SchemaLocation.java
index ecbbb3099..82f7cbe7c 100644
--- a/src/main/java/com/networknt/schema/SchemaLocation.java
+++ b/src/main/java/com/networknt/schema/SchemaLocation.java
@@ -22,6 +22,7 @@
import com.networknt.schema.path.NodePath;
import com.networknt.schema.path.PathType;
+import com.networknt.schema.utils.Strings;
/**
* The schema location is the canonical IRI of the schema object plus a JSON
@@ -219,7 +220,8 @@ public static NodePath of(String fragmentString) {
fragmentString = fragmentString.substring(1);
}
NodePath fragment = JSON_POINTER;
- String[] fragmentParts = fragmentString.split("/");
+// String[] fragmentParts = fragmentString.split("/");
+ String[] fragmentParts = Strings.split(fragmentString, '/');
boolean jsonPointer = false;
if (fragmentString.startsWith("/")) {
@@ -270,10 +272,6 @@ public static NodePath of(String fragmentString) {
fragment = fragment.append(fragmentPartString);
}
}
- if (index == -1 && fragmentString.endsWith("/")) {
- // Trailing / in fragment
- fragment = fragment.append("");
- }
return fragment;
}
diff --git a/src/main/java/com/networknt/schema/SchemaRegistry.java b/src/main/java/com/networknt/schema/SchemaRegistry.java
index dfcfb80d9..0b8af9432 100644
--- a/src/main/java/com/networknt/schema/SchemaRegistry.java
+++ b/src/main/java/com/networknt/schema/SchemaRegistry.java
@@ -22,7 +22,7 @@
import com.networknt.schema.dialect.Dialect;
import com.networknt.schema.dialect.DialectId;
import com.networknt.schema.dialect.DialectRegistry;
-import com.networknt.schema.path.NodePath;
+import com.networknt.schema.resource.InputStreamSource;
import com.networknt.schema.resource.ResourceLoaders;
import com.networknt.schema.resource.SchemaIdResolvers;
import com.networknt.schema.resource.SchemaLoader;
@@ -33,6 +33,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
@@ -454,7 +455,7 @@ public SchemaLoader getSchemaLoader() {
protected Schema newSchema(SchemaLocation schemaUri, JsonNode schemaNode) {
final SchemaContext schemaContext = createSchemaContext(schemaNode);
Schema jsonSchema = doCreate(schemaContext, getSchemaLocation(schemaUri),
- new NodePath(schemaContext.getSchemaRegistryConfig().getPathType()), schemaNode, null, false);
+ schemaNode, null, false);
preload(jsonSchema);
return jsonSchema;
}
@@ -483,14 +484,14 @@ private void preload(Schema schema) {
}
}
- public Schema create(SchemaContext schemaContext, SchemaLocation schemaLocation, NodePath evaluationPath,
+ public Schema create(SchemaContext schemaContext, SchemaLocation schemaLocation,
JsonNode schemaNode, Schema parentSchema) {
- return doCreate(schemaContext, schemaLocation, evaluationPath, schemaNode, parentSchema, false);
+ return doCreate(schemaContext, schemaLocation, schemaNode, parentSchema, false);
}
- private Schema doCreate(SchemaContext schemaContext, SchemaLocation schemaLocation, NodePath evaluationPath,
+ private Schema doCreate(SchemaContext schemaContext, SchemaLocation schemaLocation,
JsonNode schemaNode, Schema parentSchema, boolean suppressSubSchemaRetrieval) {
- return Schema.from(withDialect(schemaContext, schemaNode), schemaLocation, evaluationPath, schemaNode,
+ return Schema.from(withDialect(schemaContext, schemaNode), schemaLocation, schemaNode,
parentSchema, suppressSubSchemaRetrieval);
}
@@ -680,38 +681,42 @@ public Schema loadSchema(final SchemaLocation schemaUri) {
}
protected Schema getMappedSchema(final SchemaLocation schemaUri) {
- try (InputStream inputStream = this.schemaLoader.getSchemaResource(schemaUri.getAbsoluteIri())
- .getInputStream()) {
- if (inputStream == null) {
- throw new IOException("Cannot load schema at " + schemaUri);
- }
- final JsonNode schemaNode;
- if (isYaml(schemaUri)) {
- schemaNode = readTree(inputStream, InputFormat.YAML);
- } else {
- schemaNode = readTree(inputStream, InputFormat.JSON);
- }
+ InputStreamSource inputStreamSource = this.schemaLoader.getSchemaResource(schemaUri.getAbsoluteIri());
+ if (inputStreamSource != null) {
+ try (InputStream inputStream = inputStreamSource.getInputStream()) {
+ if (inputStream == null) {
+ throw new IOException("Cannot load schema at " + schemaUri);
+ }
+ final JsonNode schemaNode;
+ if (isYaml(schemaUri)) {
+ schemaNode = readTree(inputStream, InputFormat.YAML);
+ } else {
+ schemaNode = readTree(inputStream, InputFormat.JSON);
+ }
- final Dialect dialect = getDialectOrDefault(schemaNode);
- NodePath evaluationPath = new NodePath(getSchemaRegistryConfig().getPathType());
- if (schemaUri.getFragment() == null || schemaUri.getFragment().getNameCount() == 0) {
- // Schema without fragment
- SchemaContext schemaContext = new SchemaContext(dialect, this);
- return doCreate(schemaContext, schemaUri, evaluationPath, schemaNode, null,
- true /* retrieved via id, resolving will not change anything */);
- } else {
- // Schema with fragment pointing to sub schema
- final SchemaContext schemaContext = createSchemaContext(schemaNode);
- SchemaLocation documentLocation = new SchemaLocation(schemaUri.getAbsoluteIri());
- Schema document = doCreate(schemaContext, documentLocation, evaluationPath, schemaNode, null, false);
- return document.getRefSchema(schemaUri.getFragment());
+ final Dialect dialect = getDialectOrDefault(schemaNode);
+ if (schemaUri.getFragment() == null || schemaUri.getFragment().getNameCount() == 0) {
+ // Schema without fragment
+ SchemaContext schemaContext = new SchemaContext(dialect, this);
+ return doCreate(schemaContext, schemaUri, schemaNode, null,
+ true /* retrieved via id, resolving will not change anything */);
+ } else {
+ // Schema with fragment pointing to sub schema
+ final SchemaContext schemaContext = createSchemaContext(schemaNode);
+ SchemaLocation documentLocation = new SchemaLocation(schemaUri.getAbsoluteIri());
+ Schema document = doCreate(schemaContext, documentLocation, schemaNode, null,
+ false);
+ return document.getRefSchema(schemaUri.getFragment());
+ }
+ } catch (IOException e) {
+ logger.error("Failed to load json schema from {}", schemaUri.getAbsoluteIri(), e);
+ SchemaException exception = new SchemaException(
+ "Failed to load json schema from " + schemaUri.getAbsoluteIri());
+ exception.initCause(e);
+ throw exception;
}
- } catch (IOException e) {
- logger.error("Failed to load json schema from {}", schemaUri.getAbsoluteIri(), e);
- SchemaException exception = new SchemaException(
- "Failed to load json schema from " + schemaUri.getAbsoluteIri());
- exception.initCause(e);
- throw exception;
+ } else {
+ throw new SchemaException(new FileNotFoundException(schemaUri.getAbsoluteIri().toString()));
}
}
diff --git a/src/main/java/com/networknt/schema/SchemaRegistryConfig.java b/src/main/java/com/networknt/schema/SchemaRegistryConfig.java
index 053f03a62..65f049d4a 100644
--- a/src/main/java/com/networknt/schema/SchemaRegistryConfig.java
+++ b/src/main/java/com/networknt/schema/SchemaRegistryConfig.java
@@ -46,8 +46,6 @@ public static SchemaRegistryConfig getInstance() {
return Holder.INSTANCE;
}
- public static final int DEFAULT_PRELOAD_SCHEMA_REF_MAX_NESTING_DEPTH = 40;
-
/**
* The execution context customizer that runs by default for all schemas.
*/
@@ -75,12 +73,6 @@ public static SchemaRegistryConfig getInstance() {
*/
private final Boolean formatAssertionsEnabled;
- /**
- * When set to true, use Java-specific semantics rather than native JavaScript
- * semantics
- */
- private final boolean javaSemantics;
-
/**
* The Locale to consider when loading validation messages from the default resource bundle.
*/
@@ -106,11 +98,6 @@ public static SchemaRegistryConfig getInstance() {
*/
private final boolean preloadSchema;
- /**
- * Controls the max depth of the evaluation path to preload when preloading refs.
- */
- private final int preloadSchemaRefMaxNestingDepth;
-
/**
* Used to create {@link com.networknt.schema.regex.RegularExpression}.
*/
@@ -139,10 +126,9 @@ public static SchemaRegistryConfig getInstance() {
protected SchemaRegistryConfig(boolean cacheRefs,
String errorMessageKeyword, ExecutionContextCustomizer executionContextCustomizer, boolean failFast,
Boolean formatAssertionsEnabled,
- boolean javaSemantics,
Locale locale, boolean losslessNarrowing,
MessageSource messageSource, PathType pathType,
- boolean preloadSchema, int preloadSchemaRefMaxNestingDepth,
+ boolean preloadSchema,
RegularExpressionFactory regularExpressionFactory, SchemaIdValidator schemaIdValidator,
Map strictness, boolean typeLoose) {
super();
@@ -151,13 +137,11 @@ protected SchemaRegistryConfig(boolean cacheRefs,
this.executionContextCustomizer = executionContextCustomizer;
this.failFast = failFast;
this.formatAssertionsEnabled = formatAssertionsEnabled;
- this.javaSemantics = javaSemantics;
this.locale = locale;
this.losslessNarrowing = losslessNarrowing;
this.messageSource = messageSource;
this.pathType = pathType;
this.preloadSchema = preloadSchema;
- this.preloadSchemaRefMaxNestingDepth = preloadSchemaRefMaxNestingDepth;
this.regularExpressionFactory = regularExpressionFactory;
this.schemaIdValidator = schemaIdValidator;
this.strictness = strictness;
@@ -221,15 +205,6 @@ public PathType getPathType() {
return this.pathType;
}
- /**
- * Gets the max depth of the evaluation path to preload when preloading refs.
- *
- * @return the max depth to preload
- */
- public int getPreloadSchemaRefMaxNestingDepth() {
- return preloadSchemaRefMaxNestingDepth;
- }
-
/**
* Gets the regular expression factory.
*
@@ -269,10 +244,6 @@ public boolean isFailFast() {
return this.failFast;
}
- public boolean isJavaSemantics() {
- return this.javaSemantics;
- }
-
public boolean isLosslessNarrowing() {
return this.losslessNarrowing;
}
@@ -346,13 +317,11 @@ public static Builder builder(SchemaRegistryConfig config) {
builder.executionContextCustomizer = config.executionContextCustomizer;
builder.failFast = config.failFast;
builder.formatAssertionsEnabled = config.formatAssertionsEnabled;
- builder.javaSemantics = config.javaSemantics;
builder.locale = config.locale;
builder.losslessNarrowing = config.losslessNarrowing;
builder.messageSource = config.messageSource;
builder.pathType = config.pathType;
builder.preloadSchema = config.preloadSchema;
- builder.preloadSchemaRefMaxNestingDepth = config.preloadSchemaRefMaxNestingDepth;
builder.regularExpressionFactory = config.regularExpressionFactory;
builder.schemaIdValidator = config.schemaIdValidator;
builder.strictness = config.strictness;
@@ -379,13 +348,11 @@ public static abstract class BuilderSupport {
protected ExecutionContextCustomizer executionContextCustomizer = null;
protected boolean failFast = false;
protected Boolean formatAssertionsEnabled = null;
- protected boolean javaSemantics = false;
protected Locale locale = null; // This must be null to use Locale.getDefault() as the default can be changed
protected boolean losslessNarrowing = false;
protected MessageSource messageSource = null;
protected PathType pathType = PathType.JSON_POINTER;
protected boolean preloadSchema = true;
- protected int preloadSchemaRefMaxNestingDepth = DEFAULT_PRELOAD_SCHEMA_REF_MAX_NESTING_DEPTH;
protected RegularExpressionFactory regularExpressionFactory = JDKRegularExpressionFactory.getInstance();
protected SchemaIdValidator schemaIdValidator = SchemaIdValidator.DEFAULT;
protected Map strictness = new HashMap<>(0);
@@ -459,12 +426,7 @@ public T formatAssertionsEnabled(Boolean formatAssertionsEnabled) {
return self();
}
- public T javaSemantics(boolean javaSemantics) {
- this.javaSemantics = javaSemantics;
- return self();
- }
-
- /**
+ /**
* Set the locale to consider when generating localized messages.
*
* Note that this locale is set on a schema registry basis. To configure the
@@ -480,7 +442,16 @@ public T locale(Locale locale) {
this.locale = locale;
return self();
}
-
+ /**
+ * Sets whether lossless narrowing of other numeric types to integer is performed.
+ *
+ * Note that this likely only has a visible effect for dialects written before Draft 6.
+ *
+ * Since Draft 6 for example 1.0 is treated as an integer even without this enabled.
+ *
+ * @param losslessNarrowing true to enable
+ * @return the builder
+ */
public T losslessNarrowing(boolean losslessNarrowing) {
this.losslessNarrowing = losslessNarrowing;
return self();
@@ -519,18 +490,7 @@ public T preloadSchema(boolean preloadSchema) {
this.preloadSchema = preloadSchema;
return self();
}
- /**
- * Sets the max depth of the evaluation path to preload when preloading refs.
- *
- * Defaults to 40.
- *
- * @param preloadSchemaRefMaxNestingDepth to preload
- * @return the builder
- */
- public T preloadSchemaRefMaxNestingDepth(int preloadSchemaRefMaxNestingDepth) {
- this.preloadSchemaRefMaxNestingDepth = preloadSchemaRefMaxNestingDepth;
- return self();
- }
+
/**
* Sets the regular expression factory.
*
@@ -572,13 +532,11 @@ public T typeLoose(boolean typeLoose) {
this.typeLoose = typeLoose;
return self();
}
+
public SchemaRegistryConfig build() {
- return new SchemaRegistryConfig(cacheRefs, errorMessageKeyword,
- executionContextCustomizer, failFast, formatAssertionsEnabled,
- javaSemantics, locale, losslessNarrowing, messageSource,
- pathType, preloadSchema, preloadSchemaRefMaxNestingDepth,
- regularExpressionFactory, schemaIdValidator, strictness, typeLoose
- );
+ return new SchemaRegistryConfig(cacheRefs, errorMessageKeyword, executionContextCustomizer, failFast,
+ formatAssertionsEnabled, locale, losslessNarrowing, messageSource, pathType,
+ preloadSchema, regularExpressionFactory, schemaIdValidator, strictness, typeLoose);
}
}
diff --git a/src/main/java/com/networknt/schema/Validator.java b/src/main/java/com/networknt/schema/Validator.java
index 84d295dc5..09434d990 100644
--- a/src/main/java/com/networknt/schema/Validator.java
+++ b/src/main/java/com/networknt/schema/Validator.java
@@ -63,13 +63,4 @@ default void walk(ExecutionContext executionContext, JsonNode instanceNode, Json
* @return the schema location
*/
SchemaLocation getSchemaLocation();
-
- /**
- * The evaluation path is the set of keys, starting from the schema root,
- * through which evaluation passes to reach the schema object that produced a
- * specific result.
- *
- * @return the evaluation path
- */
- NodePath getEvaluationPath();
}
diff --git a/src/main/java/com/networknt/schema/annotation/Annotation.java b/src/main/java/com/networknt/schema/annotation/Annotation.java
index a3638498d..7d59579d0 100644
--- a/src/main/java/com/networknt/schema/annotation/Annotation.java
+++ b/src/main/java/com/networknt/schema/annotation/Annotation.java
@@ -30,6 +30,7 @@ public class Annotation {
private final SchemaLocation schemaLocation;
private final NodePath evaluationPath;
private final Object value;
+ private boolean valid = true; // If not valid it means dropped
public Annotation(String keyword, NodePath instanceLocation, SchemaLocation schemaLocation,
NodePath evaluationPath, Object value) {
@@ -90,6 +91,14 @@ public T getValue() {
return (T) value;
}
+ public boolean isValid() {
+ return valid;
+ }
+
+ public void setValid(boolean valid) {
+ this.valid = valid;
+ }
+
@Override
public String toString() {
return "Annotation [evaluationPath=" + evaluationPath + ", schemaLocation=" + schemaLocation
diff --git a/src/main/java/com/networknt/schema/dialect/Dialect.java b/src/main/java/com/networknt/schema/dialect/Dialect.java
index ebe04b2bc..3fd4580c8 100644
--- a/src/main/java/com/networknt/schema/dialect/Dialect.java
+++ b/src/main/java/com/networknt/schema/dialect/Dialect.java
@@ -30,7 +30,6 @@
import com.networknt.schema.keyword.KeywordFactory;
import com.networknt.schema.keyword.KeywordValidator;
import com.networknt.schema.keyword.UnknownKeywordFactory;
-import com.networknt.schema.path.NodePath;
import com.networknt.schema.keyword.KeywordType;
import com.networknt.schema.utils.Strings;
import com.networknt.schema.vocabulary.Vocabularies;
@@ -441,14 +440,13 @@ public SpecificationVersion getSpecificationVersion() {
*
* @param schemaContext the schema context
* @param schemaLocation the schema location
- * @param evaluationPath the evaluation path
* @param keyword the keyword
* @param schemaNode the schema node
* @param parentSchema the parent schema
* @return the validator
*/
public KeywordValidator newValidator(SchemaContext schemaContext, SchemaLocation schemaLocation,
- NodePath evaluationPath, String keyword, JsonNode schemaNode, Schema parentSchema) {
+ String keyword, JsonNode schemaNode, Schema parentSchema) {
try {
Keyword kw = this.keywords.get(keyword);
if (kw == null) {
@@ -460,7 +458,7 @@ public KeywordValidator newValidator(SchemaContext schemaContext, SchemaLocation
}
if (KeywordType.DISCRIMINATOR.getValue().equals(keyword)
&& schemaContext.isDiscriminatorKeywordEnabled()) {
- return KeywordType.DISCRIMINATOR.newValidator(schemaLocation, evaluationPath, schemaNode,
+ return KeywordType.DISCRIMINATOR.newValidator(schemaLocation, schemaNode,
parentSchema, schemaContext);
}
kw = this.builder.unknownKeywordFactory != null
@@ -470,7 +468,7 @@ public KeywordValidator newValidator(SchemaContext schemaContext, SchemaLocation
return null;
}
}
- return kw.newValidator(schemaLocation, evaluationPath, schemaNode, parentSchema, schemaContext);
+ return kw.newValidator(schemaLocation, schemaNode, parentSchema, schemaContext);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof SchemaException) {
logger.error("Error:", e);
diff --git a/src/main/java/com/networknt/schema/format/BaseFormatValidator.java b/src/main/java/com/networknt/schema/format/BaseFormatValidator.java
index 31cda4d2b..b22e17b2b 100644
--- a/src/main/java/com/networknt/schema/format/BaseFormatValidator.java
+++ b/src/main/java/com/networknt/schema/format/BaseFormatValidator.java
@@ -10,15 +10,14 @@
import com.networknt.schema.SpecificationVersion;
import com.networknt.schema.keyword.BaseKeywordValidator;
import com.networknt.schema.keyword.Keyword;
-import com.networknt.schema.path.NodePath;
public abstract class BaseFormatValidator extends BaseKeywordValidator {
protected final boolean assertionsEnabled;
- public BaseFormatValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public BaseFormatValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, Keyword keyword,
SchemaContext schemaContext) {
- super(keyword, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(keyword, schemaNode, schemaLocation, parentSchema, schemaContext);
SpecificationVersion dialect = this.schemaContext.getDialect().getSpecificationVersion();
if (dialect == null || dialect.getOrder() < SpecificationVersion.DRAFT_2019_09.getOrder()) {
assertionsEnabled = true;
diff --git a/src/main/java/com/networknt/schema/keyword/AbstractKeywordValidator.java b/src/main/java/com/networknt/schema/keyword/AbstractKeywordValidator.java
index 9de8740fe..70d323bf3 100644
--- a/src/main/java/com/networknt/schema/keyword/AbstractKeywordValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/AbstractKeywordValidator.java
@@ -16,13 +16,14 @@
package com.networknt.schema.keyword;
+import java.util.Iterator;
import java.util.function.Consumer;
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.ExecutionContext;
+import com.networknt.schema.Schema;
import com.networknt.schema.SchemaLocation;
import com.networknt.schema.annotation.Annotation;
-import com.networknt.schema.path.NodePath;
/**
* Abstract {@link KeywordValidator}.
@@ -32,20 +33,16 @@ public abstract class AbstractKeywordValidator implements KeywordValidator {
protected final JsonNode schemaNode;
protected final SchemaLocation schemaLocation;
- protected final NodePath evaluationPath;
-
/**
* Constructor.
* @param keyword the keyword
* @param schemaNode the schema node
* @param schemaLocation the schema location
- * @param evaluationPath the evaluation path
*/
- public AbstractKeywordValidator(String keyword, JsonNode schemaNode, SchemaLocation schemaLocation, NodePath evaluationPath) {
+ public AbstractKeywordValidator(String keyword, JsonNode schemaNode, SchemaLocation schemaLocation) {
this.keyword = keyword;
this.schemaNode = schemaNode;
this.schemaLocation = schemaLocation;
- this.evaluationPath = evaluationPath;
}
/**
@@ -53,10 +50,9 @@ public AbstractKeywordValidator(String keyword, JsonNode schemaNode, SchemaLocat
* @param keyword the keyword
* @param schemaNode the schema node
* @param schemaLocation the schema location
- * @param evaluationPath the evaluation path
*/
- public AbstractKeywordValidator(Keyword keyword, JsonNode schemaNode, SchemaLocation schemaLocation, NodePath evaluationPath) {
- this(keyword.getValue(), schemaNode, schemaLocation, evaluationPath);
+ public AbstractKeywordValidator(Keyword keyword, JsonNode schemaNode, SchemaLocation schemaLocation) {
+ this(keyword.getValue(), schemaNode, schemaLocation);
}
@Override
@@ -64,11 +60,6 @@ public SchemaLocation getSchemaLocation() {
return schemaLocation;
}
- @Override
- public NodePath getEvaluationPath() {
- return evaluationPath;
- }
-
@Override
public String getKeyword() {
return keyword;
@@ -85,7 +76,7 @@ public JsonNode getSchemaNode() {
@Override
public String toString() {
- return getEvaluationPath().getName(-1);
+ return getKeyword();
}
/**
@@ -117,9 +108,73 @@ protected boolean collectAnnotations(ExecutionContext executionContext, String k
* @param customizer to customize the annotation
*/
protected void putAnnotation(ExecutionContext executionContext, Consumer customizer) {
- Annotation.Builder builder = Annotation.builder().evaluationPath(this.evaluationPath)
+ Annotation.Builder builder = Annotation.builder().evaluationPath(executionContext.getEvaluationPath())
.schemaLocation(this.schemaLocation).keyword(getKeyword());
customizer.accept(builder);
executionContext.getAnnotations().put(builder.build());
}
+
+
+
+ /**
+ * Determines if the keyword exists adjacent in the evaluation path.
+ *
+ * This does not check if the keyword exists in the current meta schema as this
+ * can be a cross-draft case where the properties keyword is in a Draft 7 schema
+ * and the unevaluatedProperties keyword is in an outer Draft 2020-12 schema.
+ *
+ * The fact that the validator exists in the evaluation path implies that the
+ * keyword was valid in whatever meta schema for that schema it was created for.
+ *
+ * @param keyword the keyword to check
+ * @return true if found
+ */
+ protected boolean hasAdjacentKeywordInEvaluationPath(ExecutionContext executionContext, String keyword) {
+ Iterator evaluationSchemaPathIterator = executionContext.getEvaluationSchemaPath().descendingIterator();
+ Iterator evaluationSchemaIterator = executionContext.getEvaluationSchema().descendingIterator();
+ boolean stop = false;
+
+ // Skip the first as this is the path pointing to the current keyword eg. properties eg /$ref/properties
+ // What is needed is the evaluationPath pointing to the current evaluationSchema eg /$ref
+ if (evaluationSchemaPathIterator.hasNext()) {
+ evaluationSchemaPathIterator.next();
+ }
+
+ while (evaluationSchemaIterator.hasNext()) {
+ Schema schema = evaluationSchemaIterator.next();
+ boolean hasKeyword = schema.getSchemaNode().has(keyword);
+ if (hasKeyword) {
+ return true;
+ }
+ if (stop) {
+ return false;
+ }
+ if (evaluationSchemaPathIterator.hasNext()) {
+ Object evaluationPath = evaluationSchemaPathIterator.next();
+ if ("properties".equals(evaluationPath) || "items".equals(evaluationPath)) {
+ // If there is a change in instance location then after the next schema
+ // stop
+ stop = true;
+ }
+ }
+ }
+ return false;
+ }
+
+ protected boolean hasUnevaluatedItemsInEvaluationPath(ExecutionContext executionContext) {
+ if (executionContext.isUnevaluatedItemsPresent()
+ && hasAdjacentKeywordInEvaluationPath(executionContext, "unevaluatedItems")) {
+ return true;
+ }
+ return false;
+ }
+
+ protected boolean hasUnevaluatedPropertiesInEvaluationPath(ExecutionContext executionContext) {
+ if (executionContext.isUnevaluatedPropertiesPresent()
+ && hasAdjacentKeywordInEvaluationPath(executionContext, "unevaluatedProperties")) {
+ return true;
+ }
+ return false;
+ }
+
}
diff --git a/src/main/java/com/networknt/schema/keyword/AdditionalPropertiesValidator.java b/src/main/java/com/networknt/schema/keyword/AdditionalPropertiesValidator.java
index 6359ef392..37a53d749 100644
--- a/src/main/java/com/networknt/schema/keyword/AdditionalPropertiesValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/AdditionalPropertiesValidator.java
@@ -43,17 +43,15 @@ public class AdditionalPropertiesValidator extends BaseKeywordValidator {
private final Set allowedProperties;
private final List patternProperties;
- private Boolean hasUnevaluatedPropertiesValidator;
-
- public AdditionalPropertiesValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema,
+ public AdditionalPropertiesValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema,
SchemaContext schemaContext) {
- super(KeywordType.ADDITIONAL_PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.ADDITIONAL_PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode.isBoolean()) {
allowAdditionalProperties = schemaNode.booleanValue();
additionalPropertiesSchema = null;
} else if (schemaNode.isObject()) {
allowAdditionalProperties = true;
- additionalPropertiesSchema = schemaContext.newSchema(schemaLocation, evaluationPath, schemaNode, parentSchema);
+ additionalPropertiesSchema = schemaContext.newSchema(schemaLocation, schemaNode, parentSchema);
} else {
allowAdditionalProperties = false;
additionalPropertiesSchema = null;
@@ -95,7 +93,8 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
Set matchedInstancePropertyNames = null;
- boolean collectAnnotations = collectAnnotations() || collectAnnotations(executionContext);
+ boolean collectAnnotations = hasUnevaluatedPropertiesInEvaluationPath(executionContext)
+ || collectAnnotations(executionContext);
// if allowAdditionalProperties is true, add all the properties as evaluated.
if (allowAdditionalProperties && collectAnnotations) {
for (Iterator it = node.fieldNames(); it.hasNext();) {
@@ -118,6 +117,7 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
if (!allowAdditionalProperties) {
executionContext.addError(error().instanceNode(node).property(pname)
.instanceLocation(instanceLocation)
+ .evaluationPath(executionContext.getEvaluationPath())
.locale(executionContext.getExecutionConfig().getLocale())
.arguments(pname).build());
} else {
@@ -135,7 +135,7 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
}
if (collectAnnotations) {
executionContext.getAnnotations().put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation).keyword(getKeyword())
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation).keyword(getKeyword())
.value(matchedInstancePropertyNames != null ? matchedInstancePropertyNames : Collections.emptySet())
.build());
}
@@ -180,22 +180,10 @@ private boolean handledByPatternProperties(String pname) {
return false;
}
- private boolean collectAnnotations() {
- return hasUnevaluatedPropertiesValidator();
- }
-
- private boolean hasUnevaluatedPropertiesValidator() {
- if (this.hasUnevaluatedPropertiesValidator == null) {
- this.hasUnevaluatedPropertiesValidator = hasAdjacentKeywordInEvaluationPath("unevaluatedProperties");
- }
- return hasUnevaluatedPropertiesValidator;
- }
-
@Override
public void preloadSchema() {
if(additionalPropertiesSchema != null) {
additionalPropertiesSchema.initializeValidators();
}
- collectAnnotations(); // cache the flag
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/AllOfValidator.java b/src/main/java/com/networknt/schema/keyword/AllOfValidator.java
index 279cbd62b..c0d871d2d 100644
--- a/src/main/java/com/networknt/schema/keyword/AllOfValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/AllOfValidator.java
@@ -35,9 +35,9 @@
public class AllOfValidator extends BaseKeywordValidator {
private final List schemas;
- public AllOfValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public AllOfValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.ALL_OF, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.ALL_OF, schemaNode, schemaLocation, parentSchema, schemaContext);
if (!schemaNode.isArray()) {
JsonType nodeType = TypeFactory.getValueNodeType(schemaNode, this.schemaContext.getSchemaRegistryConfig());
throw new SchemaException(error().instanceNode(schemaNode).instanceLocation(schemaLocation.getFragment())
@@ -46,7 +46,7 @@ public AllOfValidator(SchemaLocation schemaLocation, NodePath evaluationPath, Js
int size = schemaNode.size();
this.schemas = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
- this.schemas.add(schemaContext.newSchema(schemaLocation.append(i), evaluationPath.append(i),
+ this.schemas.add(schemaContext.newSchema(schemaLocation.append(i),
schemaNode.get(i), parentSchema));
}
}
@@ -59,12 +59,19 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
protected void validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode,
NodePath instanceLocation, boolean walk) {
+ int schemaIndex = 0;
for (Schema schema : this.schemas) {
- if (!walk) {
- schema.validate(executionContext, node, rootNode, instanceLocation);
- } else {
- schema.walk(executionContext, node, rootNode, instanceLocation, true);
+ executionContext.evaluationPathAddLast(schemaIndex);
+ try {
+ if (!walk) {
+ schema.validate(executionContext, node, rootNode, instanceLocation);
+ } else {
+ schema.walk(executionContext, node, rootNode, instanceLocation, true);
+ }
+ } finally {
+ executionContext.evaluationPathRemoveLast();
}
+ schemaIndex++;
}
}
@@ -75,9 +82,16 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
validate(executionContext, node, rootNode, instanceLocation, true);
return;
}
+ int schemaIndex = 0;
for (Schema schema : this.schemas) {
// Walk through the schema
- schema.walk(executionContext, node, rootNode, instanceLocation, false);
+ executionContext.evaluationPathAddLast(schemaIndex);
+ try {
+ schema.walk(executionContext, node, rootNode, instanceLocation, false);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
+ schemaIndex++;
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/AnnotationKeyword.java b/src/main/java/com/networknt/schema/keyword/AnnotationKeyword.java
index 1a0b17af0..6c961b973 100644
--- a/src/main/java/com/networknt/schema/keyword/AnnotationKeyword.java
+++ b/src/main/java/com/networknt/schema/keyword/AnnotationKeyword.java
@@ -29,9 +29,9 @@
public class AnnotationKeyword extends AbstractKeyword {
private static final class Validator extends AbstractKeywordValidator {
- public Validator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public Validator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext, Keyword keyword) {
- super(keyword, schemaNode, schemaLocation, evaluationPath);
+ super(keyword, schemaNode, schemaLocation);
}
@Override
@@ -62,8 +62,8 @@ public AnnotationKeyword(String keyword) {
}
@Override
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- return new Validator(schemaLocation, evaluationPath, schemaNode, parentSchema, schemaContext, this);
+ return new Validator(schemaLocation, schemaNode, parentSchema, schemaContext, this);
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/AnyOfValidator.java b/src/main/java/com/networknt/schema/keyword/AnyOfValidator.java
index 45c1bf72a..d2b722ffc 100644
--- a/src/main/java/com/networknt/schema/keyword/AnyOfValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/AnyOfValidator.java
@@ -36,11 +36,9 @@
public class AnyOfValidator extends BaseKeywordValidator {
private final List schemas;
- private Boolean canShortCircuit = null;
-
- public AnyOfValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public AnyOfValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.ANY_OF, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.ANY_OF, schemaNode, schemaLocation, parentSchema, schemaContext);
if (!schemaNode.isArray()) {
JsonType nodeType = TypeFactory.getValueNodeType(schemaNode, this.schemaContext.getSchemaRegistryConfig());
throw new SchemaException(error().instanceNode(schemaNode).instanceLocation(schemaLocation.getFragment())
@@ -49,7 +47,7 @@ public AnyOfValidator(SchemaLocation schemaLocation, NodePath evaluationPath, Js
int size = schemaNode.size();
this.schemas = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
- this.schemas.add(schemaContext.newSchema(schemaLocation.append(i), evaluationPath.append(i),
+ this.schemas.add(schemaContext.newSchema(schemaLocation.append(i),
schemaNode.get(i), parentSchema));
}
}
@@ -73,28 +71,21 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
// Save flag as nested schema evaluation shouldn't trigger fail fast
boolean failFast = executionContext.isFailFast();
try {
+ int schemaIndex = 0;
executionContext.setFailFast(false);
for (Schema schema : this.schemas) {
subSchemaErrors.clear(); // Reuse and clear for each run
- TypeValidator typeValidator = schema.getTypeValidator();
- if (typeValidator != null) {
- // If schema has type validator and node type doesn't match with schemaType then
- // ignore it
- // For union type, it is a must to call TypeValidator
- if (typeValidator.getSchemaType() != JsonType.UNION && !typeValidator.equalsToSchemaType(node)) {
- typeValidator.validate(executionContext, node, rootNode, instanceLocation);
- if (allErrors == null) {
- allErrors = new ArrayList<>();
- }
- allErrors.addAll(subSchemaErrors);
- continue;
+ executionContext.evaluationPathAddLast(schemaIndex);
+ try {
+ if (!walk) {
+ schema.validate(executionContext, node, rootNode, instanceLocation);
+ } else {
+ schema.walk(executionContext, node, rootNode, instanceLocation, true);
}
+ } finally {
+ executionContext.evaluationPathRemoveLast();
}
- if (!walk) {
- schema.validate(executionContext, node, rootNode, instanceLocation);
- } else {
- schema.walk(executionContext, node, rootNode, instanceLocation, true);
- }
+ schemaIndex++;
// check if any validation errors have occurred
if (subSchemaErrors.isEmpty()) {
@@ -103,7 +94,7 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
}
if (subSchemaErrors.isEmpty() && (!this.schemaContext.isDiscriminatorKeywordEnabled())
- && canShortCircuit() && canShortCircuit(executionContext)) {
+ && canShortCircuit(executionContext)) {
// Successful so return only the existing errors, ie. no new errors
executionContext.setErrors(existingErrors);
return;
@@ -172,7 +163,7 @@ && canShortCircuit() && canShortCircuit(executionContext)) {
// generating an assertion
// if the discriminatingValue is not set in the payload
existingErrors.add(error().keyword("discriminator").instanceNode(node)
- .instanceLocation(instanceLocation).locale(executionContext.getExecutionConfig().getLocale())
+ .instanceLocation(instanceLocation).evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.messageKey("discriminator.anyOf.no_match_found").arguments(state.getDiscriminatingValue())
.build());
}
@@ -201,8 +192,15 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
validate(executionContext, node, rootNode, instanceLocation, true);
return;
}
+ int schemaIndex = 0;
for (Schema schema : this.schemas) {
- schema.walk(executionContext, node, rootNode, instanceLocation, false);
+ executionContext.evaluationPathAddLast(schemaIndex);
+ try {
+ schema.walk(executionContext, node, rootNode, instanceLocation, false);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
+ schemaIndex++;
}
}
@@ -215,31 +213,17 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
* @return true if can short circuit
*/
protected boolean canShortCircuit(ExecutionContext executionContext) {
- return !executionContext.getExecutionConfig().isAnnotationCollectionEnabled();
- }
-
- /**
- * If annotations are require for evaluation cannot short circuit.
- *
- * @return true if can short circuit
- */
- protected boolean canShortCircuit() {
- if (this.canShortCircuit == null) {
- boolean canShortCircuit = true;
- for (KeywordValidator validator : getEvaluationParentSchema().getValidators()) {
- if ("unevaluatedProperties".equals(validator.getKeyword())
- || "unevaluatedItems".equals(validator.getKeyword())) {
- canShortCircuit = false;
- }
- }
- this.canShortCircuit = canShortCircuit;
+ if (hasUnevaluatedItemsInEvaluationPath(executionContext)) {
+ return false;
+ }
+ if (hasUnevaluatedPropertiesInEvaluationPath(executionContext)) {
+ return false;
}
- return this.canShortCircuit;
+ return !executionContext.getExecutionConfig().isAnnotationCollectionEnabled();
}
@Override
public void preloadSchema() {
preloadSchemas(this.schemas);
- canShortCircuit(); // cache flag
}
}
\ No newline at end of file
diff --git a/src/main/java/com/networknt/schema/keyword/BaseKeywordValidator.java b/src/main/java/com/networknt/schema/keyword/BaseKeywordValidator.java
index c58d4b8eb..ef13efb08 100644
--- a/src/main/java/com/networknt/schema/keyword/BaseKeywordValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/BaseKeywordValidator.java
@@ -21,7 +21,6 @@
import com.networknt.schema.Schema;
import com.networknt.schema.MessageSourceError;
import com.networknt.schema.SchemaLocation;
-import com.networknt.schema.path.NodePath;
import com.networknt.schema.SchemaContext;
import java.util.Collection;
@@ -36,12 +35,9 @@ public abstract class BaseKeywordValidator extends AbstractKeywordValidator {
protected final Schema parentSchema;
protected final Map errorMessage;
- protected final Schema evaluationParentSchema;
-
public BaseKeywordValidator(Keyword keyword, JsonNode schemaNode, SchemaLocation schemaLocation,
- Schema parentSchema, SchemaContext schemaContext,
- NodePath evaluationPath) {
- super(keyword, schemaNode, schemaLocation, evaluationPath);
+ Schema parentSchema, SchemaContext schemaContext) {
+ super(keyword, schemaNode, schemaLocation);
this.schemaContext = schemaContext;
this.parentSchema = parentSchema;
@@ -51,7 +47,6 @@ public BaseKeywordValidator(Keyword keyword, JsonNode schemaNode, SchemaLocation
} else {
this.errorMessage = null;
}
- this.evaluationParentSchema = null;
}
/**
@@ -61,8 +56,6 @@ public BaseKeywordValidator(Keyword keyword, JsonNode schemaNode, SchemaLocation
* @param schemaLocation the schema location
* @param schemaContext the schema context
* @param parentSchema the parent schema
- * @param evaluationPath the evaluation path
- * @param evaluationParentSchema the evaluation parent schema
* @param errorMessage the error message
*/
protected BaseKeywordValidator(
@@ -71,16 +64,12 @@ protected BaseKeywordValidator(
SchemaLocation schemaLocation,
SchemaContext schemaContext,
Schema parentSchema,
- NodePath evaluationPath,
- Schema evaluationParentSchema,
Map errorMessage) {
- super(keyword, schemaNode, schemaLocation, evaluationPath);
+ super(keyword, schemaNode, schemaLocation);
this.schemaContext = schemaContext;
this.parentSchema = parentSchema;
this.errorMessage = errorMessage;
-
- this.evaluationParentSchema = evaluationParentSchema;
}
/**
@@ -94,21 +83,6 @@ public Schema getParentSchema() {
return this.parentSchema;
}
- /**
- * Gets the evaluation parent schema.
- *
- * This is the dynamic parent schema when following references.
- *
- * @see Schema#fromRef(Schema, NodePath)
- * @return the evaluation parent schema
- */
- public Schema getEvaluationParentSchema() {
- if (this.evaluationParentSchema != null) {
- return this.evaluationParentSchema;
- }
- return getParentSchema();
- }
-
protected String getNodeFieldType() {
JsonNode typeField = this.getParentSchema().getSchemaNode().get("type");
if (typeField != null) {
@@ -123,41 +97,10 @@ protected void preloadSchemas(final Collection schemas) {
}
}
- /**
- * Determines if the keyword exists adjacent in the evaluation path.
- *
- * This does not check if the keyword exists in the current meta schema as this
- * can be a cross-draft case where the properties keyword is in a Draft 7 schema
- * and the unevaluatedProperties keyword is in an outer Draft 2020-12 schema.
- *
- * The fact that the validator exists in the evaluation path implies that the
- * keyword was valid in whatever meta schema for that schema it was created for.
- *
- * @param keyword the keyword to check
- * @return true if found
- */
- protected boolean hasAdjacentKeywordInEvaluationPath(String keyword) {
- Schema schema = getEvaluationParentSchema();
- while (schema != null) {
- for (KeywordValidator validator : schema.getValidators()) {
- if (keyword.equals(validator.getKeyword())) {
- return true;
- }
- }
- Object element = schema.getEvaluationPath().getElement(-1);
- if ("properties".equals(element) || "items".equals(element)) {
- // If there is a change in instance location then return false
- return false;
- }
- schema = schema.getEvaluationParentSchema();
- }
- return false;
- }
-
protected MessageSourceError.Builder error() {
return MessageSourceError
.builder(this.schemaContext.getSchemaRegistryConfig().getMessageSource(), this.errorMessage)
- .schemaNode(this.schemaNode).schemaLocation(this.schemaLocation).evaluationPath(this.evaluationPath)
+ .schemaNode(this.schemaNode).schemaLocation(this.schemaLocation)
.keyword(this.getKeyword()).messageKey(this.getKeyword());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/ConstValidator.java b/src/main/java/com/networknt/schema/keyword/ConstValidator.java
index d1d1fec9f..e1c9f56bc 100644
--- a/src/main/java/com/networknt/schema/keyword/ConstValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/ConstValidator.java
@@ -26,22 +26,22 @@
* {@link KeywordValidator} for const.
*/
public class ConstValidator extends BaseKeywordValidator implements KeywordValidator {
- public ConstValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public ConstValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.CONST, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.CONST, schemaNode, schemaLocation, parentSchema, schemaContext);
}
public void validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, NodePath instanceLocation) {
if (schemaNode.isNumber() && node.isNumber()) {
if (schemaNode.decimalValue().compareTo(node.decimalValue()) != 0) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(schemaNode.asText(), node.asText())
.build());
}
} else if (!schemaNode.equals(node)) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale()).arguments(schemaNode.asText(), node.asText()).build());
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale()).arguments(schemaNode.asText(), node.asText()).build());
}
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/ContainsValidator.java b/src/main/java/com/networknt/schema/keyword/ContainsValidator.java
index 381c72cbf..801f776d5 100644
--- a/src/main/java/com/networknt/schema/keyword/ContainsValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/ContainsValidator.java
@@ -45,10 +45,8 @@ public class ContainsValidator extends BaseKeywordValidator {
private final Integer min;
private final Integer max;
- private Boolean hasUnevaluatedItemsValidator = null;
-
- public ContainsValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.CONTAINS, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public ContainsValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.CONTAINS, schemaNode, schemaLocation, parentSchema, schemaContext);
// Draft 6 added the contains keyword but maxContains and minContains first
// appeared in Draft 2019-09 so the semantics of the validation changes
@@ -58,7 +56,7 @@ public ContainsValidator(SchemaLocation schemaLocation, NodePath evaluationPath,
Integer currentMax = null;
Integer currentMin = null;
if (schemaNode.isObject() || schemaNode.isBoolean()) {
- this.schema = schemaContext.newSchema(schemaLocation, evaluationPath, schemaNode, parentSchema);
+ this.schema = schemaContext.newSchema(schemaLocation, schemaNode, parentSchema);
JsonNode parentSchemaNode = parentSchema.getSchemaNode();
Optional maxNode = Optional
.ofNullable(parentSchemaNode.get(KeywordType.MAX_CONTAINS.getValue()))
@@ -126,7 +124,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
}
}
- boolean collectAnnotations = collectAnnotations();
+ boolean collectAnnotations = hasUnevaluatedItemsInEvaluationPath(executionContext);
if (this.schema != null) {
// This keyword produces an annotation value which is an array of the indexes to
// which this keyword validates successfully when applying its subschema, in
@@ -140,12 +138,12 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
// evaluated all
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword("contains").value(true).build());
} else {
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword("contains").value(indexes).build());
}
}
@@ -157,22 +155,26 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
// Omitted keywords MUST NOT produce annotation results. However, as described
// in the section for contains, the absence of this keyword's annotation causes
// contains to assume a minimum value of 1.
+ executionContext.evaluationPathAddLast(minContainsKeyword);
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath.append(minContainsKeyword))
+ .evaluationPath(executionContext.getEvaluationPath())
.schemaLocation(this.schemaLocation.append(minContainsKeyword))
.keyword(minContainsKeyword).value(this.min).build());
+ executionContext.evaluationPathRemoveLast();
}
}
if (this.max != null) {
String maxContainsKeyword = "maxContains";
if (collectAnnotations || collectAnnotations(executionContext, maxContainsKeyword)) {
+ executionContext.evaluationPathAddLast(maxContainsKeyword);
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath.append(maxContainsKeyword))
+ .evaluationPath(executionContext.getEvaluationPath())
.schemaLocation(this.schemaLocation.append(maxContainsKeyword))
.keyword(maxContainsKeyword).value(this.max).build());
+ executionContext.evaluationPathRemoveLast();
}
}
}
@@ -181,7 +183,6 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
@Override
public void preloadSchema() {
Optional.ofNullable(this.schema).ifPresent(Schema::initializeValidators);
- collectAnnotations(); // cache the flag
}
private void boundsViolated(ExecutionContext executionContext, KeywordType validatorTypeCode, Locale locale,
@@ -192,26 +193,9 @@ private void boundsViolated(ExecutionContext executionContext, KeywordType valid
} else if (KeywordType.MAX_CONTAINS.equals(validatorTypeCode)) {
messageKey = CONTAINS_MAX;
}
- executionContext.addError(error().instanceNode(instanceNode).instanceLocation(instanceLocation).messageKey(messageKey)
+ executionContext.addError(error().instanceNode(instanceNode).instanceLocation(instanceLocation).evaluationPath(executionContext.getEvaluationPath()).messageKey(messageKey)
.locale(locale).arguments(String.valueOf(bounds), this.schema.getSchemaNode().toString())
.keyword(validatorTypeCode.getValue()).build());
}
- /**
- * Determine if annotations must be collected for evaluation.
- *
- * This will be collected regardless of whether it is needed for reporting.
- *
- * @return true if annotations must be collected for evaluation.
- */
- private boolean collectAnnotations() {
- return hasUnevaluatedItemsValidator();
- }
-
- private boolean hasUnevaluatedItemsValidator() {
- if (this.hasUnevaluatedItemsValidator == null) {
- this.hasUnevaluatedItemsValidator = hasAdjacentKeywordInEvaluationPath("unevaluatedItems");
- }
- return hasUnevaluatedItemsValidator;
- }
}
diff --git a/src/main/java/com/networknt/schema/keyword/ContentEncodingValidator.java b/src/main/java/com/networknt/schema/keyword/ContentEncodingValidator.java
index d384c9fee..758ffc420 100644
--- a/src/main/java/com/networknt/schema/keyword/ContentEncodingValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/ContentEncodingValidator.java
@@ -40,15 +40,13 @@ public class ContentEncodingValidator extends BaseKeywordValidator {
* Constructor.
*
* @param schemaLocation the schema location
- * @param evaluationPath the evaluation path
* @param schemaNode the schema node
* @param parentSchema the parent schema
* @param schemaContext the schema context
*/
- public ContentEncodingValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public ContentEncodingValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.CONTENT_ENCODING, schemaNode, schemaLocation, parentSchema, schemaContext,
- evaluationPath);
+ super(KeywordType.CONTENT_ENCODING, schemaNode, schemaLocation, parentSchema, schemaContext);
this.contentEncoding = schemaNode.textValue();
}
@@ -81,7 +79,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (!matches(node.asText())) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(this.contentEncoding)
.build());
}
diff --git a/src/main/java/com/networknt/schema/keyword/ContentMediaTypeValidator.java b/src/main/java/com/networknt/schema/keyword/ContentMediaTypeValidator.java
index f4fe1044a..98e9aae71 100644
--- a/src/main/java/com/networknt/schema/keyword/ContentMediaTypeValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/ContentMediaTypeValidator.java
@@ -45,14 +45,13 @@ public class ContentMediaTypeValidator extends BaseKeywordValidator {
* Constructor.
*
* @param schemaLocation the schema location
- * @param evaluationPath the evaluation path
* @param schemaNode the schema node
* @param parentSchema the parent schema
* @param schemaContext the schema context
*/
- public ContentMediaTypeValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public ContentMediaTypeValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.CONTENT_MEDIA_TYPE, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.CONTENT_MEDIA_TYPE, schemaNode, schemaLocation, parentSchema, schemaContext);
this.contentMediaType = schemaNode.textValue();
}
@@ -104,7 +103,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (!matches(node.asText())) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(this.contentMediaType)
.build());
}
diff --git a/src/main/java/com/networknt/schema/keyword/DependenciesValidator.java b/src/main/java/com/networknt/schema/keyword/DependenciesValidator.java
index c3426f70c..af18b6293 100644
--- a/src/main/java/com/networknt/schema/keyword/DependenciesValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/DependenciesValidator.java
@@ -36,14 +36,13 @@ public class DependenciesValidator extends BaseKeywordValidator implements Keywo
* Constructor.
*
* @param schemaLocation the schema location
- * @param evaluationPath the evaluation path
* @param schemaNode the schema node
* @param parentSchema the parent schema
* @param schemaContext the schema context
*/
- public DependenciesValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ public DependenciesValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.DEPENDENCIES, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.DEPENDENCIES, schemaNode, schemaLocation, parentSchema, schemaContext);
for (Iterator it = schemaNode.fieldNames(); it.hasNext(); ) {
String pname = it.next();
@@ -59,7 +58,7 @@ public DependenciesValidator(SchemaLocation schemaLocation, NodePath evaluationP
}
} else if (pvalue.isObject() || pvalue.isBoolean()) {
schemaDeps.put(pname, schemaContext.newSchema(schemaLocation.append(pname),
- evaluationPath.append(pname), pvalue, parentSchema));
+ pvalue, parentSchema));
}
}
}
@@ -72,14 +71,19 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
for (String field : deps) {
if (node.get(field) == null) {
executionContext.addError(error().instanceNode(node).property(pname).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(propertyDeps.toString()).build());
}
}
}
Schema schema = schemaDeps.get(pname);
if (schema != null) {
- schema.validate(executionContext, node, rootNode, instanceLocation);
+ executionContext.evaluationPathAddLast(pname);
+ try {
+ schema.validate(executionContext, node, rootNode, instanceLocation);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
}
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/DependentRequired.java b/src/main/java/com/networknt/schema/keyword/DependentRequired.java
index d6848c438..88384a368 100644
--- a/src/main/java/com/networknt/schema/keyword/DependentRequired.java
+++ b/src/main/java/com/networknt/schema/keyword/DependentRequired.java
@@ -31,9 +31,9 @@
public class DependentRequired extends BaseKeywordValidator implements KeywordValidator {
private final Map> propertyDependencies = new HashMap<>();
- public DependentRequired(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ public DependentRequired(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.DEPENDENT_REQUIRED, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.DEPENDENT_REQUIRED, schemaNode, schemaLocation, parentSchema, schemaContext);
for (Iterator it = schemaNode.fieldNames(); it.hasNext(); ) {
String pname = it.next();
@@ -56,7 +56,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
for (String field : dependencies) {
if (node.get(field) == null) {
executionContext.addError(error().instanceNode(node).property(pname).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(field, pname)
.build());
}
diff --git a/src/main/java/com/networknt/schema/keyword/DependentSchemas.java b/src/main/java/com/networknt/schema/keyword/DependentSchemas.java
index fb27538d2..8f87bdc9f 100644
--- a/src/main/java/com/networknt/schema/keyword/DependentSchemas.java
+++ b/src/main/java/com/networknt/schema/keyword/DependentSchemas.java
@@ -31,16 +31,15 @@
public class DependentSchemas extends BaseKeywordValidator {
private final Map schemaDependencies = new HashMap<>();
- public DependentSchemas(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
-
- super(KeywordType.DEPENDENT_SCHEMAS, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
-
- for (Iterator it = schemaNode.fieldNames(); it.hasNext(); ) {
+ public DependentSchemas(SchemaLocation schemaLocation, JsonNode schemaNode,
+ Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.DEPENDENT_SCHEMAS, schemaNode, schemaLocation, parentSchema, schemaContext);
+ for (Iterator it = schemaNode.fieldNames(); it.hasNext();) {
String pname = it.next();
JsonNode pvalue = schemaNode.get(pname);
if (pvalue.isObject() || pvalue.isBoolean()) {
this.schemaDependencies.put(pname, schemaContext.newSchema(schemaLocation.append(pname),
- evaluationPath.append(pname), pvalue, parentSchema));
+ pvalue, parentSchema));
}
}
}
@@ -57,10 +56,15 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
String pname = it.next();
Schema schema = this.schemaDependencies.get(pname);
if (schema != null) {
- if(!walk) {
- schema.validate(executionContext, node, rootNode, instanceLocation);
- } else {
- schema.walk(executionContext, node, rootNode, instanceLocation, true);
+ executionContext.evaluationPathAddLast(pname);
+ try {
+ if(!walk) {
+ schema.validate(executionContext, node, rootNode, instanceLocation);
+ } else {
+ schema.walk(executionContext, node, rootNode, instanceLocation, true);
+ }
+ } finally {
+ executionContext.evaluationPathRemoveLast();
}
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/DiscriminatorValidator.java b/src/main/java/com/networknt/schema/keyword/DiscriminatorValidator.java
index cdce9ac1d..bc85d0011 100644
--- a/src/main/java/com/networknt/schema/keyword/DiscriminatorValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/DiscriminatorValidator.java
@@ -63,9 +63,9 @@ public class DiscriminatorValidator extends BaseKeywordValidator {
*/
private final String defaultMapping;
- public DiscriminatorValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public DiscriminatorValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.DISCRIMINATOR, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.DISCRIMINATOR, schemaNode, schemaLocation, parentSchema, schemaContext);
ObjectNode discriminator = schemaNode.isObject() ? (ObjectNode) schemaNode : null;
if (discriminator != null) {
JsonNode propertyName = discriminator.get("propertyName");
@@ -194,7 +194,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
*/
if (this.schemaContext.getSchemaRegistryConfig().isStrict("discriminator", Boolean.FALSE)) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.messageKey("discriminator.missing_discriminating_value").arguments(this.propertyName).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/DynamicRefValidator.java b/src/main/java/com/networknt/schema/keyword/DynamicRefValidator.java
index 35648c6f4..fc6d497dc 100644
--- a/src/main/java/com/networknt/schema/keyword/DynamicRefValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/DynamicRefValidator.java
@@ -21,65 +21,59 @@
import com.networknt.schema.ExecutionContext;
import com.networknt.schema.InvalidSchemaRefException;
import com.networknt.schema.Schema;
-import com.networknt.schema.SchemaException;
import com.networknt.schema.SchemaRef;
import com.networknt.schema.path.NodePath;
import com.networknt.schema.utils.ThreadSafeCachingSupplier;
import com.networknt.schema.SchemaLocation;
import com.networknt.schema.SchemaContext;
+import java.util.Iterator;
import java.util.function.Supplier;
/**
* {@link KeywordValidator} that resolves $dynamicRef.
*/
public class DynamicRefValidator extends BaseKeywordValidator {
- protected final SchemaRef schema;
- public DynamicRefValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.DYNAMIC_REF, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
- String refValue = schemaNode.asText();
- this.schema = getRefSchema(parentSchema, schemaContext, refValue, evaluationPath);
+ public DynamicRefValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.DYNAMIC_REF, schemaNode, schemaLocation, parentSchema, schemaContext);
}
- static SchemaRef getRefSchema(Schema parentSchema, SchemaContext schemaContext, String refValue,
- NodePath evaluationPath) {
+ static SchemaRef getRefSchema(Schema parentSchema, String refValue,
+ ExecutionContext executionContext) {
String ref = resolve(parentSchema, refValue);
return new SchemaRef(getSupplier(() -> {
- Schema refSchema = schemaContext.getDynamicAnchors().get(ref);
+ SchemaContext schemaContext = parentSchema.getSchemaContext();
+ Schema refSchema = parentSchema.getSchemaContext().getDynamicAnchors().get(ref);
if (refSchema == null) { // This is a $dynamicRef without a matching $dynamicAnchor
// A $dynamicRef without a matching $dynamicAnchor in the same schema resource
// behaves like a normal $ref to $anchor
// A $dynamicRef without anchor in fragment behaves identical to $ref
- SchemaRef r = RefValidator.getRefSchema(parentSchema, schemaContext, refValue, evaluationPath);
+ SchemaRef r = RefValidator.getRefSchema(parentSchema, schemaContext, refValue);
if (r != null) {
refSchema = r.getSchema();
}
} else {
// Check parents
- Schema base = parentSchema;
+ Schema base;
int index = ref.indexOf("#");
String anchor = ref.substring(index);
String absoluteIri = ref.substring(0, index);
- while (base.getEvaluationParentSchema() != null) {
- base = base.getEvaluationParentSchema();
+ for (Iterator iter = executionContext.getEvaluationSchema().descendingIterator(); iter.hasNext();) {
+ base = iter.next();
String baseAbsoluteIri = base.getSchemaLocation().getAbsoluteIri() != null ? base.getSchemaLocation().getAbsoluteIri().toString() : "";
if (!baseAbsoluteIri.equals(absoluteIri)) {
absoluteIri = baseAbsoluteIri;
String parentRef = SchemaLocation.resolve(base.getSchemaLocation(), anchor);
- Schema parentRefSchema = schemaContext.getDynamicAnchors().get(parentRef);
+ Schema parentRefSchema = base.getSchemaContext().getDynamicAnchors().get(parentRef);
if (parentRefSchema != null) {
refSchema = parentRefSchema;
}
}
}
}
-
- if (refSchema != null) {
- refSchema = refSchema.fromRef(parentSchema, evaluationPath);
- }
return refSchema;
- }, schemaContext.getSchemaRegistryConfig().isCacheRefs()));
+ }, false));
}
static Supplier getSupplier(Supplier supplier, boolean cache) {
@@ -97,11 +91,11 @@ private static String resolve(Schema parentSchema, String refValue) {
@Override
public void validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, NodePath instanceLocation) {
- Schema refSchema = this.schema.getSchema();
+ Schema refSchema = getSchemaRef(executionContext).getSchema();
if (refSchema == null) {
Error error = error().keyword(KeywordType.DYNAMIC_REF.getValue())
.messageKey("internal.unresolvedRef").message("Reference {0} cannot be resolved")
- .instanceLocation(instanceLocation).evaluationPath(getEvaluationPath())
+ .instanceLocation(instanceLocation).evaluationPath(executionContext.getEvaluationPath())
.arguments(schemaNode.asText()).build();
throw new InvalidSchemaRefException(error);
}
@@ -113,21 +107,20 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
// This is important because if we use same JsonSchemaFactory for creating multiple JSONSchema instances,
// these schemas will be cached along with config. We have to replace the config for cached $ref references
// with the latest config. Reset the config.
- Schema refSchema = this.schema.getSchema();
+ Schema refSchema = getSchemaRef(executionContext).getSchema();
if (refSchema == null) {
Error error = error().keyword(KeywordType.DYNAMIC_REF.getValue())
.messageKey("internal.unresolvedRef").message("Reference {0} cannot be resolved")
- .instanceLocation(instanceLocation).evaluationPath(getEvaluationPath())
+ .instanceLocation(instanceLocation).evaluationPath(executionContext.getEvaluationPath())
.arguments(schemaNode.asText()).build();
throw new InvalidSchemaRefException(error);
}
if (node == null) {
// Check for circular dependency
- SchemaLocation schemaLocation = refSchema.getSchemaLocation();
- Schema check = refSchema;
boolean circularDependency = false;
- while (check.getEvaluationParentSchema() != null) {
- check = check.getEvaluationParentSchema();
+ SchemaLocation schemaLocation = refSchema.getSchemaLocation();
+ for (Iterator iter = executionContext.getEvaluationSchema().descendingIterator(); iter.hasNext();) {
+ Schema check = iter.next();
if (check.getSchemaLocation().equals(schemaLocation)) {
circularDependency = true;
break;
@@ -140,39 +133,8 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
refSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
}
- public SchemaRef getSchemaRef() {
- return this.schema;
+ public SchemaRef getSchemaRef(ExecutionContext executionContext ) {
+ String refValue = schemaNode.asText();
+ return getRefSchema(this.getParentSchema(), refValue, executionContext);
}
-
- @Override
- public void preloadSchema() {
- Schema jsonSchema = null;
- try {
- jsonSchema = this.schema.getSchema();
- } catch (SchemaException e) {
- throw e;
- } catch (RuntimeException e) {
- throw new SchemaException(e);
- }
- // Check for circular dependency
- // Only one cycle is pre-loaded
- // The rest of the cycles will load at execution time depending on the input
- // data
- SchemaLocation schemaLocation = jsonSchema.getSchemaLocation();
- Schema check = jsonSchema;
- boolean circularDependency = false;
- int depth = 0;
- while (check.getEvaluationParentSchema() != null) {
- depth++;
- check = check.getEvaluationParentSchema();
- if (check.getSchemaLocation().equals(schemaLocation)) {
- circularDependency = true;
- break;
- }
- }
- if (this.schemaContext.getSchemaRegistryConfig().isCacheRefs() && !circularDependency
- && depth < this.schemaContext.getSchemaRegistryConfig().getPreloadSchemaRefMaxNestingDepth()) {
- jsonSchema.initializeValidators();
- }
- }
}
diff --git a/src/main/java/com/networknt/schema/keyword/EnumValidator.java b/src/main/java/com/networknt/schema/keyword/EnumValidator.java
index 6ad8d9d50..8ee06e794 100644
--- a/src/main/java/com/networknt/schema/keyword/EnumValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/EnumValidator.java
@@ -48,8 +48,8 @@ static String asText(JsonNode node) {
return node.asText();
}
- public EnumValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.ENUM, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public EnumValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.ENUM, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode != null && schemaNode.isArray()) {
nodes = new HashSet<>();
StringBuilder sb = new StringBuilder();
@@ -100,7 +100,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
}
if (!nodes.contains(node) && !( this.schemaContext.getSchemaRegistryConfig().isTypeLoose() && isTypeLooseContainsInEnum(node))) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(error).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/ExclusiveMaximumValidator.java b/src/main/java/com/networknt/schema/keyword/ExclusiveMaximumValidator.java
index bc05fd9ae..d7aa9f5d7 100644
--- a/src/main/java/com/networknt/schema/keyword/ExclusiveMaximumValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/ExclusiveMaximumValidator.java
@@ -35,8 +35,8 @@
public class ExclusiveMaximumValidator extends BaseKeywordValidator {
private final ThresholdMixin typedMaximum;
- public ExclusiveMaximumValidator(SchemaLocation schemaLocation, NodePath evaluationPath, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.EXCLUSIVE_MAXIMUM, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public ExclusiveMaximumValidator(SchemaLocation schemaLocation, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.EXCLUSIVE_MAXIMUM, schemaNode, schemaLocation, parentSchema, schemaContext);
if (!schemaNode.isNumber()) {
throw new SchemaException("exclusiveMaximum value is not a number");
}
@@ -105,7 +105,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (typedMaximum.crossesThreshold(node)) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(typedMaximum.thresholdValue()).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/ExclusiveMinimumValidator.java b/src/main/java/com/networknt/schema/keyword/ExclusiveMinimumValidator.java
index 297259121..200965fd7 100644
--- a/src/main/java/com/networknt/schema/keyword/ExclusiveMinimumValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/ExclusiveMinimumValidator.java
@@ -39,8 +39,8 @@ public class ExclusiveMinimumValidator extends BaseKeywordValidator {
*/
private final ThresholdMixin typedMinimum;
- public ExclusiveMinimumValidator(SchemaLocation schemaLocation, NodePath evaluationPath, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.EXCLUSIVE_MINIMUM, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public ExclusiveMinimumValidator(SchemaLocation schemaLocation, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.EXCLUSIVE_MINIMUM, schemaNode, schemaLocation, parentSchema, schemaContext);
if (!schemaNode.isNumber()) {
throw new SchemaException("exclusiveMinimum value is not a number");
}
@@ -112,7 +112,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (typedMinimum.crossesThreshold(node)) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(typedMinimum.thresholdValue()).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/FalseValidator.java b/src/main/java/com/networknt/schema/keyword/FalseValidator.java
index dd1aaf578..49025e2c1 100644
--- a/src/main/java/com/networknt/schema/keyword/FalseValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/FalseValidator.java
@@ -18,25 +18,24 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.ExecutionContext;
import com.networknt.schema.Schema;
+import com.networknt.schema.SchemaContext;
import com.networknt.schema.SchemaLocation;
import com.networknt.schema.path.NodePath;
-import com.networknt.schema.SchemaContext;
/**
* {@link KeywordValidator} for false.
*/
public class FalseValidator extends BaseKeywordValidator implements KeywordValidator {
- private final String reason;
- public FalseValidator(SchemaLocation schemaLocation, NodePath evaluationPath, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.FALSE, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
- this.reason = this.evaluationPath.getParent().getName(-1);
+ public FalseValidator(SchemaLocation schemaLocation, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.FALSE, schemaNode, schemaLocation, parentSchema, schemaContext);
}
public void validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, NodePath instanceLocation) {
// For the false validator, it is always not valid
+ String reason = executionContext.getEvaluationPath().getParent().getName(-1);
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(reason).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/FormatKeyword.java b/src/main/java/com/networknt/schema/keyword/FormatKeyword.java
index 1c21e4f13..6e35bc112 100644
--- a/src/main/java/com/networknt/schema/keyword/FormatKeyword.java
+++ b/src/main/java/com/networknt/schema/keyword/FormatKeyword.java
@@ -20,7 +20,6 @@
import com.networknt.schema.Schema;
import com.networknt.schema.SchemaLocation;
import com.networknt.schema.format.Format;
-import com.networknt.schema.path.NodePath;
import com.networknt.schema.SchemaContext;
import java.util.Collection;
@@ -52,13 +51,13 @@ Collection getFormats() {
}
@Override
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ public KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
Format format = null;
if (schemaNode != null && schemaNode.isTextual()) {
String formatName = schemaNode.textValue();
format = this.formats.get(formatName);
}
- return new FormatValidator(schemaLocation, evaluationPath, schemaNode, parentSchema, schemaContext, format,
+ return new FormatValidator(schemaLocation, schemaNode, parentSchema, schemaContext, format,
this);
}
diff --git a/src/main/java/com/networknt/schema/keyword/FormatValidator.java b/src/main/java/com/networknt/schema/keyword/FormatValidator.java
index aa26df6d4..f9a6c5814 100644
--- a/src/main/java/com/networknt/schema/keyword/FormatValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/FormatValidator.java
@@ -38,10 +38,10 @@ public class FormatValidator extends BaseFormatValidator implements KeywordValid
private final Format format;
- public FormatValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public FormatValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext, Format format,
Keyword keyword) {
- super(schemaLocation, evaluationPath, schemaNode, parentSchema, keyword, schemaContext);
+ super(schemaLocation, schemaNode, parentSchema, keyword, schemaContext);
this.format = format;
}
@@ -76,7 +76,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
format.validate(executionContext, schemaContext, node, rootNode, instanceLocation,
assertionsEnabled,
() -> this.error().instanceNode(node).instanceLocation(instanceLocation)
- .messageKey(format.getMessageKey())
+ .evaluationPath(executionContext.getEvaluationPath()).messageKey(format.getMessageKey())
.locale(executionContext.getExecutionConfig().getLocale())
,
this);
@@ -106,7 +106,7 @@ protected void validateUnknownFormat(ExecutionContext executionContext,
*/
if (createUnknownFormatAssertions(executionContext) && this.schemaNode.isTextual()) {
executionContext.addError(error().instanceLocation(instanceLocation).instanceNode(node)
- .messageKey("format.unknown").arguments(schemaNode.textValue()).build());
+ .evaluationPath(executionContext.getEvaluationPath()).messageKey("format.unknown").arguments(schemaNode.textValue()).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/IfValidator.java b/src/main/java/com/networknt/schema/keyword/IfValidator.java
index 65923bdc6..22e5053be 100644
--- a/src/main/java/com/networknt/schema/keyword/IfValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/IfValidator.java
@@ -36,8 +36,8 @@ public class IfValidator extends BaseKeywordValidator {
private final Schema thenSchema;
private final Schema elseSchema;
- public IfValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.IF_THEN_ELSE, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public IfValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.IF_THEN_ELSE, schemaNode, schemaLocation, parentSchema, schemaContext);
Schema foundIfSchema = null;
Schema foundThenSchema = null;
@@ -46,15 +46,14 @@ public IfValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonN
for (final String keyword : KEYWORDS) {
final JsonNode node = parentSchema.getSchemaNode().get(keyword);
final SchemaLocation schemaLocationOfSchema = parentSchema.getSchemaLocation().append(keyword);
- final NodePath evaluationPathOfSchema = parentSchema.getEvaluationPath().append(keyword);
if (keyword.equals("if")) {
- foundIfSchema = schemaContext.newSchema(schemaLocationOfSchema, evaluationPathOfSchema, node,
+ foundIfSchema = schemaContext.newSchema(schemaLocationOfSchema, node,
parentSchema);
} else if (keyword.equals("then") && node != null) {
- foundThenSchema = schemaContext.newSchema(schemaLocationOfSchema, evaluationPathOfSchema, node,
+ foundThenSchema = schemaContext.newSchema(schemaLocationOfSchema, node,
parentSchema);
} else if (keyword.equals("else") && node != null) {
- foundElseSchema = schemaContext.newSchema(schemaLocationOfSchema, evaluationPathOfSchema, node,
+ foundElseSchema = schemaContext.newSchema(schemaLocationOfSchema, node,
parentSchema);
}
}
@@ -66,8 +65,6 @@ public IfValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonN
@Override
public void validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, NodePath instanceLocation) {
-
-
boolean ifConditionPassed = false;
// Save flag as nested schema evaluation shouldn't trigger fail fast
@@ -86,9 +83,25 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
}
if (ifConditionPassed && this.thenSchema != null) {
- this.thenSchema.validate(executionContext, node, rootNode, instanceLocation);
+ // The "if" keyword is a bit unusual as it actually handles multiple keywords
+ // This removes the "if" in the evaluation path so the rest of the evaluation paths will be correct
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("then");
+ try {
+ this.thenSchema.validate(executionContext, node, rootNode, instanceLocation);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("if");
+ }
} else if (!ifConditionPassed && this.elseSchema != null) {
- this.elseSchema.validate(executionContext, node, rootNode, instanceLocation);
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("else");
+ try {
+ this.elseSchema.validate(executionContext, node, rootNode, instanceLocation);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("if");
+ }
}
}
@@ -107,6 +120,9 @@ public void preloadSchema() {
@Override
public void walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, NodePath instanceLocation, boolean shouldValidateSchema) {
+ // The "if" keyword is a bit unusual as it actually handles multiple keywords
+ // This removes the "if" in the evaluation path so the rest of the evaluation paths will be correct
+
boolean checkCondition = node != null && shouldValidateSchema;
boolean ifConditionPassed = false;
@@ -126,17 +142,44 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
}
if (!checkCondition) {
if (this.thenSchema != null) {
- this.thenSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("then");
+ try {
+ this.thenSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("if");
+ }
}
if (this.elseSchema != null) {
- this.elseSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("else");
+ try {
+ this.elseSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("if");
+ }
}
} else {
if (this.thenSchema != null && ifConditionPassed) {
- this.thenSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
- }
- else if (this.elseSchema != null && !ifConditionPassed) {
- this.elseSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("then");
+ try {
+ this.thenSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("if");
+ }
+ } else if (this.elseSchema != null && !ifConditionPassed) {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("else");
+ try {
+ this.elseSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("if");
+ }
}
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/ItemsLegacyValidator.java b/src/main/java/com/networknt/schema/keyword/ItemsLegacyValidator.java
index 92ead9533..85e5c7d7e 100644
--- a/src/main/java/com/networknt/schema/keyword/ItemsLegacyValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/ItemsLegacyValidator.java
@@ -16,19 +16,21 @@
package com.networknt.schema.keyword;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.networknt.schema.ExecutionContext;
import com.networknt.schema.Schema;
-import com.networknt.schema.SchemaRef;
-import com.networknt.schema.SchemaLocation;
import com.networknt.schema.SchemaContext;
+import com.networknt.schema.SchemaLocation;
+import com.networknt.schema.SchemaRef;
import com.networknt.schema.annotation.Annotation;
import com.networknt.schema.path.NodePath;
import com.networknt.schema.utils.SchemaRefs;
-import java.util.*;
-
/**
* {@link KeywordValidator} for items Draft 4 to Draft 2019-09.
*/
@@ -40,14 +42,11 @@ public class ItemsLegacyValidator extends BaseKeywordValidator {
private final Boolean additionalItems;
private final Schema additionalSchema;
- private Boolean hasUnevaluatedItemsValidator = null;
-
- private final NodePath additionalItemsEvaluationPath;
private final SchemaLocation additionalItemsSchemaLocation;
private final JsonNode additionalItemsSchemaNode;
- public ItemsLegacyValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.ITEMS_LEGACY, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public ItemsLegacyValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.ITEMS_LEGACY, schemaNode, schemaLocation, parentSchema, schemaContext);
Boolean additionalItems = null;
@@ -56,13 +55,13 @@ public ItemsLegacyValidator(SchemaLocation schemaLocation, NodePath evaluationPa
JsonNode additionalItemsSchemaNode = null;
if (schemaNode.isObject() || schemaNode.isBoolean()) {
- foundSchema = schemaContext.newSchema(schemaLocation, evaluationPath, schemaNode, parentSchema);
+ foundSchema = schemaContext.newSchema(schemaLocation, schemaNode, parentSchema);
this.tupleSchema = Collections.emptyList();
} else {
int i = 0;
this.tupleSchema = new ArrayList<>(schemaNode.size());
for (JsonNode s : schemaNode) {
- this.tupleSchema.add(schemaContext.newSchema(schemaLocation.append(i), evaluationPath.append(i),
+ this.tupleSchema.add(schemaContext.newSchema(schemaLocation.append(i),
s, parentSchema));
i++;
}
@@ -75,14 +74,13 @@ public ItemsLegacyValidator(SchemaLocation schemaLocation, NodePath evaluationPa
} else if (addItemNode.isObject()) {
foundAdditionalSchema = schemaContext.newSchema(
parentSchema.getSchemaLocation().append(PROPERTY_ADDITIONAL_ITEMS),
- parentSchema.getEvaluationPath().append(PROPERTY_ADDITIONAL_ITEMS), addItemNode, parentSchema);
+ addItemNode, parentSchema);
}
}
}
this.additionalItems = additionalItems;
this.schema = foundSchema;
this.additionalSchema = foundAdditionalSchema;
- this.additionalItemsEvaluationPath = parentSchema.getEvaluationPath().append(PROPERTY_ADDITIONAL_ITEMS);
this.additionalItemsSchemaLocation = parentSchema.getSchemaLocation().append(PROPERTY_ADDITIONAL_ITEMS);
this.additionalItemsSchemaNode = additionalItemsSchemaNode;
}
@@ -95,7 +93,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
// ignores non-arrays
return;
}
- boolean collectAnnotations = collectAnnotations();
+ boolean collectAnnotations = hasUnevaluatedItemsInEvaluationPath(executionContext);
// Add items annotation
if (collectAnnotations || collectAnnotations(executionContext)) {
@@ -103,7 +101,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
// Applies to all
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(true).build());
} else if (this.tupleSchema != null) {
// Tuples
@@ -113,13 +111,13 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
// More items than schemas so the keyword only applied to the number of schemas
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(schemas).build());
} else {
// Applies to all
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(true).build());
}
}
@@ -142,11 +140,13 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (hasAdditionalItem) {
if (collectAnnotations || collectAnnotations(executionContext, "additionalItems")) {
+ executionContext.evaluationPathAddLast("additionalItems");
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.additionalItemsEvaluationPath)
+ .evaluationPath(executionContext.getEvaluationPath())
.schemaLocation(this.additionalItemsSchemaLocation)
.keyword("additionalItems").value(true).build());
+ executionContext.evaluationPathRemoveLast();
}
}
}
@@ -163,7 +163,13 @@ private boolean doValidate(ExecutionContext executionContext, int i, JsonNode no
} else if (this.tupleSchema != null) {
if (i < this.tupleSchema.size()) {
// validate against tuple schema
- this.tupleSchema.get(i).validate(executionContext, node, rootNode, path);
+ executionContext.evaluationPathAddLast(i);
+ try {
+ this.tupleSchema.get(i).validate(executionContext, node, rootNode, path);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
+
} else {
if ((this.additionalItems != null && this.additionalItems) || this.additionalSchema != null) {
isAdditionalItem = true;
@@ -171,21 +177,35 @@ private boolean doValidate(ExecutionContext executionContext, int i, JsonNode no
if (this.additionalSchema != null) {
// validate against additional item schema
- this.additionalSchema.validate(executionContext, node, rootNode, path);
+ executionContext.evaluationPathRemoveLast(); // remove items
+ executionContext.evaluationPathAddLast("additionalItems");
+ try {
+ this.additionalSchema.validate(executionContext, node, rootNode, path);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("items");
+ }
} else if (this.additionalItems != null) {
if (this.additionalItems) {
// evaluatedItems.add(path);
} else {
// no additional item allowed, return error
- executionContext.addError(error().instanceNode(rootNode).instanceLocation(instanceLocation)
- .keyword("additionalItems")
- .messageKey("additionalItems")
- .evaluationPath(this.additionalItemsEvaluationPath)
- .schemaLocation(this.additionalItemsSchemaLocation)
- .schemaNode(this.additionalItemsSchemaNode)
- .locale(executionContext.getExecutionConfig().getLocale())
- .index(i)
- .arguments(i).build());
+ executionContext.evaluationPathRemoveLast(); // remove items
+ executionContext.evaluationPathAddLast("additionalItems");
+ try {
+ executionContext.addError(error().instanceNode(rootNode).instanceLocation(instanceLocation)
+ .keyword("additionalItems")
+ .messageKey("additionalItems")
+ .evaluationPath(executionContext.getEvaluationPath())
+ .schemaLocation(this.additionalItemsSchemaLocation)
+ .schemaNode(this.additionalItemsSchemaNode)
+ .locale(executionContext.getExecutionConfig().getLocale())
+ .index(i)
+ .arguments(i).build());
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("items");
+ }
}
}
}
@@ -195,7 +215,7 @@ private boolean doValidate(ExecutionContext executionContext, int i, JsonNode no
@Override
public void walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, NodePath instanceLocation, boolean shouldValidateSchema) {
- boolean collectAnnotations = collectAnnotations();
+ boolean collectAnnotations = hasUnevaluatedItemsInEvaluationPath(executionContext);
// Add items annotation
if (collectAnnotations || collectAnnotations(executionContext)) {
@@ -203,7 +223,7 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
// Applies to all
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(true).build());
} else if (this.tupleSchema != null) {
// Tuples
@@ -213,13 +233,13 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
// More items than schemas so the keyword only applied to the number of schemas
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(schemas).build());
} else {
// Applies to all
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(true).build());
}
}
@@ -232,7 +252,7 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
ArrayNode arrayNode = (ArrayNode) node;
JsonNode defaultNode = null;
if (executionContext.getWalkConfig().getApplyDefaultsStrategy().shouldApplyArrayDefaults()) {
- defaultNode = getDefaultNode(this.schema);
+ defaultNode = getDefaultNode(this.schema, executionContext);
}
for (int i = 0; i < count; i++) {
JsonNode n = arrayNode.get(i);
@@ -257,7 +277,7 @@ else if (this.tupleSchema != null) {
JsonNode defaultNode = null;
JsonNode n = arrayNode.get(i);
if (executionContext.getWalkConfig().getApplyDefaultsStrategy().shouldApplyArrayDefaults()) {
- defaultNode = getDefaultNode(this.tupleSchema.get(i));
+ defaultNode = getDefaultNode(this.tupleSchema.get(i), executionContext);
}
if (n != null) {
if (n.isNull() && defaultNode != null) {
@@ -265,11 +285,21 @@ else if (this.tupleSchema != null) {
n = defaultNode;
}
}
- walkSchema(executionContext, this.tupleSchema.get(i), n, rootNode, instanceLocation.append(i),
- shouldValidateSchema, KeywordType.ITEMS_LEGACY.getValue());
+ executionContext.evaluationPathAddLast(i);
+ try {
+ walkSchema(executionContext, this.tupleSchema.get(i), n, rootNode, instanceLocation.append(i),
+ shouldValidateSchema, KeywordType.ITEMS_LEGACY.getValue());
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
} else {
- walkSchema(executionContext, this.tupleSchema.get(i), null, rootNode, instanceLocation.append(i),
- shouldValidateSchema, KeywordType.ITEMS_LEGACY.getValue());
+ executionContext.evaluationPathAddLast(i);
+ try {
+ walkSchema(executionContext, this.tupleSchema.get(i), null, rootNode,
+ instanceLocation.append(i), shouldValidateSchema, KeywordType.ITEMS_LEGACY.getValue());
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
}
}
if (this.additionalSchema != null) {
@@ -284,7 +314,7 @@ else if (this.tupleSchema != null) {
JsonNode defaultNode = null;
JsonNode n = arrayNode.get(i);
if (executionContext.getWalkConfig().getApplyDefaultsStrategy().shouldApplyArrayDefaults()) {
- defaultNode = getDefaultNode(this.additionalSchema);
+ defaultNode = getDefaultNode(this.additionalSchema, executionContext);
}
if (n != null) {
if (n.isNull() && defaultNode != null) {
@@ -305,23 +335,25 @@ else if (this.tupleSchema != null) {
if (hasAdditionalItem) {
if (collectAnnotations || collectAnnotations(executionContext, "additionalItems")) {
+ executionContext.evaluationPathAddLast("additionalItems");
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.additionalItemsEvaluationPath)
+ .evaluationPath(executionContext.getEvaluationPath())
.schemaLocation(this.additionalItemsSchemaLocation)
.keyword("additionalItems").value(true).build());
+ executionContext.evaluationPathRemoveLast();
}
}
}
}
}
- private static JsonNode getDefaultNode(Schema schema) {
+ private static JsonNode getDefaultNode(Schema schema, ExecutionContext executionContext) {
JsonNode result = schema.getSchemaNode().get("default");
if (result == null) {
- SchemaRef schemaRef = SchemaRefs.from(schema);
+ SchemaRef schemaRef = SchemaRefs.from(schema, executionContext);
if (schemaRef != null) {
- result = getDefaultNode(schemaRef.getSchema());
+ result = getDefaultNode(schemaRef.getSchema(), executionContext);
}
}
return result;
@@ -329,15 +361,27 @@ private static JsonNode getDefaultNode(Schema schema) {
private void walkSchema(ExecutionContext executionContext, Schema walkSchema, JsonNode node, JsonNode rootNode,
NodePath instanceLocation, boolean shouldValidateSchema, String keyword) {
- boolean executeWalk = executionContext.getWalkConfig().getItemWalkListenerRunner().runPreWalkListeners(executionContext, keyword,
- node, rootNode, instanceLocation, walkSchema, this);
- int currentErrors = executionContext.getErrors().size();
- if (executeWalk) {
- walkSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ boolean additionalItems = "additionalItems".equals(keyword);
+ if (additionalItems) {
+ executionContext.evaluationPathRemoveLast(); // remove items
+ executionContext.evaluationPathAddLast(keyword);
+ }
+ try {
+ boolean executeWalk = executionContext.getWalkConfig().getItemWalkListenerRunner()
+ .runPreWalkListeners(executionContext, keyword, node, rootNode, instanceLocation, walkSchema, this);
+ int currentErrors = executionContext.getErrors().size();
+ if (executeWalk) {
+ walkSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ }
+ executionContext.getWalkConfig().getItemWalkListenerRunner().runPostWalkListeners(executionContext, keyword,
+ node, rootNode, instanceLocation, walkSchema, this,
+ executionContext.getErrors().subList(currentErrors, executionContext.getErrors().size()));
+ } finally {
+ if (additionalItems) {
+ executionContext.evaluationPathRemoveLast();
+ executionContext.evaluationPathAddLast("items");
+ }
}
- executionContext.getWalkConfig().getItemWalkListenerRunner().runPostWalkListeners(executionContext, keyword, node, rootNode,
- instanceLocation, walkSchema, this, executionContext.getErrors().subList(currentErrors, executionContext.getErrors().size()));
-
}
public List getTupleSchema() {
@@ -348,17 +392,6 @@ public Schema getSchema() {
return this.schema;
}
- private boolean collectAnnotations() {
- return hasUnevaluatedItemsValidator();
- }
-
- private boolean hasUnevaluatedItemsValidator() {
- if (this.hasUnevaluatedItemsValidator == null) {
- this.hasUnevaluatedItemsValidator = hasAdjacentKeywordInEvaluationPath("unevaluatedItems");
- }
- return hasUnevaluatedItemsValidator;
- }
-
@Override
public void preloadSchema() {
if (null != this.schema) {
@@ -368,6 +401,5 @@ public void preloadSchema() {
if (null != this.additionalSchema) {
this.additionalSchema.initializeValidators();
}
- collectAnnotations(); // cache the flag
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/networknt/schema/keyword/ItemsValidator.java b/src/main/java/com/networknt/schema/keyword/ItemsValidator.java
index 644f2e62d..5877edaf3 100644
--- a/src/main/java/com/networknt/schema/keyword/ItemsValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/ItemsValidator.java
@@ -35,12 +35,9 @@ public class ItemsValidator extends BaseKeywordValidator {
private final int prefixCount;
private final boolean additionalItems;
- private Boolean hasUnevaluatedItemsValidator = null;
-
- public ItemsValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public ItemsValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext,
- evaluationPath);
+ super(KeywordType.ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext);
JsonNode prefixItems = parentSchema.getSchemaNode().get("prefixItems");
if (prefixItems instanceof ArrayNode) {
@@ -52,7 +49,7 @@ public ItemsValidator(SchemaLocation schemaLocation, NodePath evaluationPath, Js
}
if (schemaNode.isObject() || schemaNode.isBoolean()) {
- this.schema = schemaContext.newSchema(schemaLocation, evaluationPath, schemaNode, parentSchema);
+ this.schema = schemaContext.newSchema(schemaLocation, schemaNode, parentSchema);
} else {
throw new IllegalArgumentException("The value of 'items' MUST be a valid JSON Schema.");
}
@@ -78,17 +75,17 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
// generate a helpful message
int x = i;
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.index(x).arguments(x).build());
}
evaluated = true;
}
if (evaluated) {
- if (collectAnnotations() || collectAnnotations(executionContext)) {
+ if (hasUnevaluatedItemsInEvaluationPath(executionContext) || collectAnnotations(executionContext)) {
// Applies to all
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(true).build());
}
}
@@ -103,7 +100,7 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
JsonNode defaultNode = null;
if (executionContext.getWalkConfig().getApplyDefaultsStrategy().shouldApplyArrayDefaults()
&& this.schema != null) {
- defaultNode = getDefaultNode(this.schema);
+ defaultNode = getDefaultNode(this.schema, executionContext);
}
boolean evaluated = false;
for (int i = this.prefixCount; i < node.size(); ++i) {
@@ -119,11 +116,11 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
}
}
if (evaluated) {
- if (collectAnnotations() || collectAnnotations(executionContext)) {
+ if (hasUnevaluatedItemsInEvaluationPath(executionContext) || collectAnnotations(executionContext)) {
// Applies to all
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(true).build());
}
}
@@ -135,12 +132,12 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
}
}
- private static JsonNode getDefaultNode(Schema schema) {
+ private static JsonNode getDefaultNode(Schema schema, ExecutionContext executionContext) {
JsonNode result = schema.getSchemaNode().get("default");
if (result == null) {
- SchemaRef schemaRef = SchemaRefs.from(schema);
+ SchemaRef schemaRef = SchemaRefs.from(schema, executionContext);
if (schemaRef != null) {
- result = getDefaultNode(schemaRef.getSchema());
+ result = getDefaultNode(schemaRef.getSchema(), executionContext);
}
}
return result;
@@ -151,7 +148,7 @@ private void walkSchema(ExecutionContext executionContext, Schema walkSchema, Js
//@formatter:off
boolean executeWalk = executionContext.getWalkConfig().getItemWalkListenerRunner().runPreWalkListeners(
executionContext,
- KeywordType.ITEMS_LEGACY.getValue(),
+ KeywordType.ITEMS.getValue(),
node,
rootNode,
instanceLocation,
@@ -163,7 +160,7 @@ private void walkSchema(ExecutionContext executionContext, Schema walkSchema, Js
}
executionContext.getWalkConfig().getItemWalkListenerRunner().runPostWalkListeners(
executionContext,
- KeywordType.ITEMS_LEGACY.getValue(),
+ KeywordType.ITEMS.getValue(),
node,
rootNode,
instanceLocation,
@@ -180,18 +177,5 @@ public Schema getSchema() {
@Override
public void preloadSchema() {
this.schema.initializeValidators();
- collectAnnotations(); // cache the flag
- }
-
- private boolean collectAnnotations() {
- return hasUnevaluatedItemsValidator();
}
-
- private boolean hasUnevaluatedItemsValidator() {
- if (this.hasUnevaluatedItemsValidator == null) {
- this.hasUnevaluatedItemsValidator = hasAdjacentKeywordInEvaluationPath("unevaluatedItems");
- }
- return hasUnevaluatedItemsValidator;
- }
-
}
diff --git a/src/main/java/com/networknt/schema/keyword/Keyword.java b/src/main/java/com/networknt/schema/keyword/Keyword.java
index 93a01b666..596c7ea62 100644
--- a/src/main/java/com/networknt/schema/keyword/Keyword.java
+++ b/src/main/java/com/networknt/schema/keyword/Keyword.java
@@ -20,7 +20,6 @@
import com.networknt.schema.Schema;
import com.networknt.schema.SchemaException;
import com.networknt.schema.SchemaLocation;
-import com.networknt.schema.path.NodePath;
import com.networknt.schema.SchemaContext;
/**
@@ -38,7 +37,6 @@ public interface Keyword {
* Creates a new validator for the keyword.
*
* @param schemaLocation the schema location
- * @param evaluationPath the evaluation path
* @param schemaNode the schema node
* @param parentSchema the parent schema
* @param schemaContext the schema context
@@ -46,6 +44,6 @@ public interface Keyword {
* @throws SchemaException the exception
* @throws Exception the exception
*/
- KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) throws SchemaException, Exception;
}
diff --git a/src/main/java/com/networknt/schema/keyword/KeywordType.java b/src/main/java/com/networknt/schema/keyword/KeywordType.java
index d89e4b1ca..a199cde1b 100644
--- a/src/main/java/com/networknt/schema/keyword/KeywordType.java
+++ b/src/main/java/com/networknt/schema/keyword/KeywordType.java
@@ -21,7 +21,6 @@
import com.networknt.schema.SchemaLocation;
import com.networknt.schema.SpecificationVersion;
import com.networknt.schema.SpecificationVersionRange;
-import com.networknt.schema.path.NodePath;
import com.networknt.schema.SchemaContext;
import java.util.ArrayList;
@@ -31,7 +30,7 @@
@FunctionalInterface
interface ValidatorFactory {
- KeywordValidator newInstance(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ KeywordValidator newInstance(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext);
}
@@ -53,7 +52,7 @@ public enum KeywordType implements Keyword {
EXCLUSIVE_MINIMUM("exclusiveMinimum", ExclusiveMinimumValidator::new, SpecificationVersionRange.DRAFT_6_TO_DRAFT_7),
FALSE("false", FalseValidator::new, SpecificationVersionRange.MIN_DRAFT_6),
FORMAT("format", null, SpecificationVersionRange.MAX_DRAFT_7) {
- @Override public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ @Override public KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
throw new UnsupportedOperationException("Use FormatKeyword instead");
}
},
@@ -131,12 +130,12 @@ public static KeywordType fromValue(String value) {
}
@Override
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
if (this.validatorFactory == null) {
throw new UnsupportedOperationException("No suitable validator for " + getValue());
}
- return validatorFactory.newInstance(schemaLocation, evaluationPath, schemaNode, parentSchema,
+ return validatorFactory.newInstance(schemaLocation, schemaNode, parentSchema,
schemaContext);
}
diff --git a/src/main/java/com/networknt/schema/keyword/MaxItemsValidator.java b/src/main/java/com/networknt/schema/keyword/MaxItemsValidator.java
index 11b405213..049101155 100644
--- a/src/main/java/com/networknt/schema/keyword/MaxItemsValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/MaxItemsValidator.java
@@ -29,8 +29,8 @@
public class MaxItemsValidator extends BaseKeywordValidator implements KeywordValidator {
private final int max;
- public MaxItemsValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.MAX_ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public MaxItemsValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.MAX_ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode.canConvertToExactIntegral()) {
this.max = schemaNode.intValue();
} else {
@@ -44,13 +44,13 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (node.isArray()) {
if (node.size() > this.max) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(this.max, node.size()).build());
}
} else if (this.schemaContext.getSchemaRegistryConfig().isTypeLoose()) {
if (1 > this.max) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(this.max, 1).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/MaxLengthValidator.java b/src/main/java/com/networknt/schema/keyword/MaxLengthValidator.java
index bb6312092..0e1cda817 100644
--- a/src/main/java/com/networknt/schema/keyword/MaxLengthValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/MaxLengthValidator.java
@@ -31,8 +31,8 @@
public class MaxLengthValidator extends BaseKeywordValidator implements KeywordValidator {
private final int maxLength;
- public MaxLengthValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.MAX_LENGTH, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public MaxLengthValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.MAX_LENGTH, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode != null && schemaNode.canConvertToExactIntegral()) {
this.maxLength = schemaNode.intValue();
} else {
@@ -50,7 +50,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
}
if (node.textValue().codePointCount(0, node.textValue().length()) > this.maxLength) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(this.maxLength).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/MaxPropertiesValidator.java b/src/main/java/com/networknt/schema/keyword/MaxPropertiesValidator.java
index 20719d936..edef1cd95 100644
--- a/src/main/java/com/networknt/schema/keyword/MaxPropertiesValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/MaxPropertiesValidator.java
@@ -29,9 +29,9 @@
public class MaxPropertiesValidator extends BaseKeywordValidator implements KeywordValidator {
private final int max;
- public MaxPropertiesValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema,
+ public MaxPropertiesValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema,
SchemaContext schemaContext) {
- super(KeywordType.MAX_PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.MAX_PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode.canConvertToExactIntegral()) {
max = schemaNode.intValue();
} else {
@@ -45,7 +45,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (node.isObject()) {
if (node.size() > max) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(max).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/MaximumValidator.java b/src/main/java/com/networknt/schema/keyword/MaximumValidator.java
index c7d6e3a78..8b5db4096 100644
--- a/src/main/java/com/networknt/schema/keyword/MaximumValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/MaximumValidator.java
@@ -40,8 +40,8 @@ public class MaximumValidator extends BaseKeywordValidator {
private final ThresholdMixin typedMaximum;
- public MaximumValidator(SchemaLocation schemaLocation, NodePath evaluationPath, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.MAXIMUM, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public MaximumValidator(SchemaLocation schemaLocation, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.MAXIMUM, schemaNode, schemaLocation, parentSchema, schemaContext);
if (!schemaNode.isNumber()) {
throw new SchemaException("maximum value is not a number");
}
@@ -120,7 +120,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (this.typedMaximum.crossesThreshold(node)) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(this.typedMaximum.thresholdValue()).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/MinItemsValidator.java b/src/main/java/com/networknt/schema/keyword/MinItemsValidator.java
index 48181b1a5..64a1b8aae 100644
--- a/src/main/java/com/networknt/schema/keyword/MinItemsValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/MinItemsValidator.java
@@ -29,8 +29,8 @@
public class MinItemsValidator extends BaseKeywordValidator implements KeywordValidator {
private int min = 0;
- public MinItemsValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.MIN_ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public MinItemsValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.MIN_ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode.canConvertToExactIntegral()) {
min = schemaNode.intValue();
}
@@ -42,14 +42,14 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (node.isArray()) {
if (node.size() < min) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(min, node.size())
.build());
}
} else if (this.schemaContext.getSchemaRegistryConfig().isTypeLoose()) {
if (1 < min) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(min, 1).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/MinLengthValidator.java b/src/main/java/com/networknt/schema/keyword/MinLengthValidator.java
index 456367bb9..c19f70709 100644
--- a/src/main/java/com/networknt/schema/keyword/MinLengthValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/MinLengthValidator.java
@@ -31,8 +31,8 @@
public class MinLengthValidator extends BaseKeywordValidator implements KeywordValidator {
private int minLength;
- public MinLengthValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.MIN_LENGTH, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public MinLengthValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.MIN_LENGTH, schemaNode, schemaLocation, parentSchema, schemaContext);
minLength = Integer.MIN_VALUE;
if (schemaNode != null && schemaNode.canConvertToExactIntegral()) {
minLength = schemaNode.intValue();
@@ -50,7 +50,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (node.textValue().codePointCount(0, node.textValue().length()) < minLength) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(minLength).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/MinMaxContainsValidator.java b/src/main/java/com/networknt/schema/keyword/MinMaxContainsValidator.java
index 68d0b3564..2ba469004 100644
--- a/src/main/java/com/networknt/schema/keyword/MinMaxContainsValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/MinMaxContainsValidator.java
@@ -20,9 +20,9 @@
public class MinMaxContainsValidator extends BaseKeywordValidator {
private final Set analysis;
- public MinMaxContainsValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema,
+ public MinMaxContainsValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema,
SchemaContext schemaContext) {
- super(KeywordType.MAX_CONTAINS, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.MAX_CONTAINS, schemaNode, schemaLocation, parentSchema, schemaContext);
Set analysis = null;
int min = 1;
@@ -68,6 +68,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
this.analysis.stream()
.map(analysis -> error().instanceNode(node)
.instanceLocation(instanceLocation)
+ .evaluationPath(executionContext.getEvaluationPath())
.messageKey(analysis.getMessageKey()).locale(executionContext.getExecutionConfig().getLocale())
.keyword(analysis.getMessageKey())
.arguments(parentSchema.getSchemaNode().toString()).build())
diff --git a/src/main/java/com/networknt/schema/keyword/MinPropertiesValidator.java b/src/main/java/com/networknt/schema/keyword/MinPropertiesValidator.java
index bf6142746..69fea4e8a 100644
--- a/src/main/java/com/networknt/schema/keyword/MinPropertiesValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/MinPropertiesValidator.java
@@ -29,9 +29,9 @@
public class MinPropertiesValidator extends BaseKeywordValidator implements KeywordValidator {
protected final int min;
- public MinPropertiesValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema,
+ public MinPropertiesValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema,
SchemaContext schemaContext) {
- super(KeywordType.MIN_PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.MIN_PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode.canConvertToExactIntegral()) {
min = schemaNode.intValue();
} else {
@@ -45,7 +45,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (node.isObject()) {
if (node.size() < min) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(min).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/MinimumValidator.java b/src/main/java/com/networknt/schema/keyword/MinimumValidator.java
index 2d9f7f3cd..627b6895c 100644
--- a/src/main/java/com/networknt/schema/keyword/MinimumValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/MinimumValidator.java
@@ -43,8 +43,8 @@ public class MinimumValidator extends BaseKeywordValidator {
*/
private final ThresholdMixin typedMinimum;
- public MinimumValidator(SchemaLocation schemaLocation, NodePath evaluationPath, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.MINIMUM, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public MinimumValidator(SchemaLocation schemaLocation, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.MINIMUM, schemaNode, schemaLocation, parentSchema, schemaContext);
if (!schemaNode.isNumber()) {
throw new SchemaException("minimum value is not a number");
@@ -127,7 +127,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (this.typedMinimum.crossesThreshold(node)) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(this.typedMinimum.thresholdValue()).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/MultipleOfValidator.java b/src/main/java/com/networknt/schema/keyword/MultipleOfValidator.java
index a69c41121..a2326601c 100644
--- a/src/main/java/com/networknt/schema/keyword/MultipleOfValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/MultipleOfValidator.java
@@ -32,9 +32,9 @@
public class MultipleOfValidator extends BaseKeywordValidator implements KeywordValidator {
private final BigDecimal divisor;
- public MultipleOfValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public MultipleOfValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.MULTIPLE_OF, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.MULTIPLE_OF, schemaNode, schemaLocation, parentSchema, schemaContext);
this.divisor = getDivisor(schemaNode);
}
@@ -46,7 +46,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (dividend != null) {
if (dividend.divideAndRemainder(this.divisor)[1].abs().compareTo(BigDecimal.ZERO) > 0) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(this.divisor)
.build());
}
diff --git a/src/main/java/com/networknt/schema/keyword/NonValidationKeyword.java b/src/main/java/com/networknt/schema/keyword/NonValidationKeyword.java
index 83c857e0c..e49236084 100644
--- a/src/main/java/com/networknt/schema/keyword/NonValidationKeyword.java
+++ b/src/main/java/com/networknt/schema/keyword/NonValidationKeyword.java
@@ -32,21 +32,21 @@
public class NonValidationKeyword extends AbstractKeyword {
private static final class Validator extends AbstractKeywordValidator {
- public Validator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public Validator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext, Keyword keyword) {
- super(keyword, schemaNode, schemaLocation, evaluationPath);
+ super(keyword, schemaNode, schemaLocation);
String id = schemaContext.resolveSchemaId(schemaNode);
String anchor = schemaContext.getDialect().readAnchor(schemaNode);
String dynamicAnchor = schemaContext.getDialect().readDynamicAnchor(schemaNode);
if (id != null || anchor != null || dynamicAnchor != null) {
// Used to register schema resources with $id
- schemaContext.newSchema(schemaLocation, evaluationPath, schemaNode, parentSchema);
+ schemaContext.newSchema(schemaLocation, schemaNode, parentSchema);
}
if ("$defs".equals(keyword.getValue()) || "definitions".equals(keyword.getValue())) {
for (Iterator> field = schemaNode.fields(); field.hasNext(); ) {
Entry property = field.next();
SchemaLocation location = schemaLocation.append(property.getKey());
- Schema schema = schemaContext.newSchema(location, evaluationPath.append(property.getKey()),
+ Schema schema = schemaContext.newSchema(location,
property.getValue(), parentSchema);
schemaContext.getSchemaReferences().put(location.toString(), schema);
}
@@ -64,8 +64,8 @@ public NonValidationKeyword(String keyword) {
}
@Override
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- return new Validator(schemaLocation, evaluationPath, schemaNode, parentSchema, schemaContext, this);
+ return new Validator(schemaLocation, schemaNode, parentSchema, schemaContext, this);
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/NotAllowedValidator.java b/src/main/java/com/networknt/schema/keyword/NotAllowedValidator.java
index 332650609..7f09b7382 100644
--- a/src/main/java/com/networknt/schema/keyword/NotAllowedValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/NotAllowedValidator.java
@@ -31,8 +31,8 @@
public class NotAllowedValidator extends BaseKeywordValidator implements KeywordValidator {
private final List fieldNames;
- public NotAllowedValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.NOT_ALLOWED, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public NotAllowedValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.NOT_ALLOWED, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode.isArray()) {
int size = schemaNode.size();
this.fieldNames = new ArrayList<>(size);
@@ -52,7 +52,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (propertyNode != null) {
executionContext.addError(error().property(fieldName).instanceNode(node)
- .instanceLocation(instanceLocation.append(fieldName))
+ .evaluationPath(executionContext.getEvaluationPath()).instanceLocation(instanceLocation.append(fieldName))
.locale(executionContext.getExecutionConfig().getLocale())
.arguments(fieldName).build());
}
diff --git a/src/main/java/com/networknt/schema/keyword/NotValidator.java b/src/main/java/com/networknt/schema/keyword/NotValidator.java
index 36b53c760..a183c7636 100644
--- a/src/main/java/com/networknt/schema/keyword/NotValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/NotValidator.java
@@ -32,9 +32,9 @@
public class NotValidator extends BaseKeywordValidator {
private final Schema schema;
- public NotValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.NOT, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
- this.schema = schemaContext.newSchema(schemaLocation, evaluationPath, schemaNode, parentSchema);
+ public NotValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.NOT, schemaNode, schemaLocation, parentSchema, schemaContext);
+ this.schema = schemaContext.newSchema(schemaLocation, schemaNode, parentSchema);
}
@Override
@@ -67,7 +67,7 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
}
if (test.isEmpty()) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(this.schemaNode.toString())
.build());
}
diff --git a/src/main/java/com/networknt/schema/keyword/OneOfValidator.java b/src/main/java/com/networknt/schema/keyword/OneOfValidator.java
index eee38b822..4358daadf 100644
--- a/src/main/java/com/networknt/schema/keyword/OneOfValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/OneOfValidator.java
@@ -36,11 +36,9 @@
public class OneOfValidator extends BaseKeywordValidator {
private final List schemas;
- private Boolean canShortCircuit = null;
-
- public OneOfValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public OneOfValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.ONE_OF, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.ONE_OF, schemaNode, schemaLocation, parentSchema, schemaContext);
if (!schemaNode.isArray()) {
JsonType nodeType = TypeFactory.getValueNodeType(schemaNode, this.schemaContext.getSchemaRegistryConfig());
throw new SchemaException(error().instanceNode(schemaNode).instanceLocation(schemaLocation.getFragment())
@@ -50,7 +48,7 @@ public OneOfValidator(SchemaLocation schemaLocation, NodePath evaluationPath, Js
this.schemas = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
JsonNode childNode = schemaNode.get(i);
- this.schemas.add(schemaContext.newSchema(schemaLocation.append(i), evaluationPath.append(i), childNode,
+ this.schemas.add(schemaContext.newSchema(schemaLocation.append(i), childNode,
parentSchema));
}
}
@@ -75,15 +73,23 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
executionContext.setErrors(subSchemaErrors);
// Save flag as nested schema evaluation shouldn't trigger fail fast
boolean failFast = executionContext.isFailFast();
+ Boolean canShortCircuit = null;
+ int schemaIndex = 0;
try {
executionContext.setFailFast(false);
for (Schema schema : this.schemas) {
subSchemaErrors.clear();
- if (!walk) {
- schema.validate(executionContext, node, rootNode, instanceLocation);
- } else {
- schema.walk(executionContext, node, rootNode, instanceLocation, true);
+ executionContext.evaluationPathAddLast(schemaIndex);
+ try {
+ if (!walk) {
+ schema.validate(executionContext, node, rootNode, instanceLocation);
+ } else {
+ schema.walk(executionContext, node, rootNode, instanceLocation, true);
+ }
+ } finally {
+ executionContext.evaluationPathRemoveLast();
}
+ schemaIndex++;
// check if any validation errors have occurred
if (subSchemaErrors.isEmpty()) { // No new errors
@@ -94,11 +100,16 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
indexes.add(Integer.toString(index));
}
- if (numberOfValidSchema > 1 && canShortCircuit()) {
- // short-circuit
- // note that the short circuit means that only 2 valid schemas are reported even
- // if could be more
- break;
+ if (numberOfValidSchema > 1) {
+ if (canShortCircuit == null) {
+ canShortCircuit = canShortCircuit(executionContext);
+ }
+ if (canShortCircuit) {
+ // short-circuit
+ // note that the short circuit means that only 2 valid schemas are reported even
+ // if could be more
+ break;
+ }
}
if (this.schemaContext.isDiscriminatorKeywordEnabled()) {
@@ -168,7 +179,7 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
// if the discriminatingValue is not set in the payload
existingErrors
.add(error().keyword("discriminator").instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.messageKey("discriminator.oneOf.no_match_found")
.arguments(state.getDiscriminatingValue()).build());
}
@@ -185,7 +196,7 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
*/
Error message = error().instanceNode(node).instanceLocation(instanceLocation)
.messageKey(numberOfValidSchema > 1 ? "oneOf.indexes" : "oneOf")
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(Integer.toString(numberOfValidSchema),
numberOfValidSchema > 1 ? String.join(", ", indexes) : "")
.build();
@@ -213,18 +224,14 @@ protected boolean reportChildErrors(ExecutionContext executionContext) {
return !executionContext.getExecutionConfig().isFailFast();
}
- protected boolean canShortCircuit() {
- if (this.canShortCircuit == null) {
- boolean canShortCircuit = true;
- for (KeywordValidator validator : getEvaluationParentSchema().getValidators()) {
- if ("unevaluatedProperties".equals(validator.getKeyword())
- || "unevaluatedItems".equals(validator.getKeyword())) {
- canShortCircuit = false;
- }
- }
- this.canShortCircuit = canShortCircuit;
+ protected boolean canShortCircuit(ExecutionContext executionContext) {
+ if (hasUnevaluatedItemsInEvaluationPath(executionContext)) {
+ return false;
}
- return this.canShortCircuit;
+ if (hasUnevaluatedPropertiesInEvaluationPath(executionContext)) {
+ return false;
+ }
+ return !executionContext.getExecutionConfig().isAnnotationCollectionEnabled();
}
@Override
@@ -233,8 +240,15 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
if (shouldValidateSchema && node != null) {
validate(executionContext, node, rootNode, instanceLocation, true);
} else {
+ int schemaIndex = 0;
for (Schema schema : this.schemas) {
- schema.walk(executionContext, node, rootNode, instanceLocation, false);
+ executionContext.evaluationPathAddLast(schemaIndex);
+ try {
+ schema.walk(executionContext, node, rootNode, instanceLocation, false);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
+ schemaIndex++;
}
}
}
@@ -244,6 +258,5 @@ public void preloadSchema() {
for (Schema schema : this.schemas) {
schema.initializeValidators();
}
- canShortCircuit(); // cache the flag
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/PatternPropertiesValidator.java b/src/main/java/com/networknt/schema/keyword/PatternPropertiesValidator.java
index 2af8ee6d2..6b262ed79 100644
--- a/src/main/java/com/networknt/schema/keyword/PatternPropertiesValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/PatternPropertiesValidator.java
@@ -34,11 +34,9 @@ public class PatternPropertiesValidator extends BaseKeywordValidator {
public static final String PROPERTY = "patternProperties";
private final Map schemas = new IdentityHashMap<>();
- private Boolean hasUnevaluatedPropertiesValidator = null;
-
- public PatternPropertiesValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema,
+ public PatternPropertiesValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema,
SchemaContext schemaContext) {
- super(KeywordType.PATTERN_PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(KeywordType.PATTERN_PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext);
if (!schemaNode.isObject()) {
throw new SchemaException("patternProperties must be an object node");
}
@@ -46,7 +44,7 @@ public PatternPropertiesValidator(SchemaLocation schemaLocation, NodePath evalua
while (names.hasNext()) {
String name = names.next();
RegularExpression pattern = RegularExpression.compile(name, schemaContext);
- schemas.put(pattern, schemaContext.newSchema(schemaLocation.append(name), evaluationPath.append(name),
+ schemas.put(pattern, schemaContext.newSchema(schemaLocation.append(name),
schemaNode.get(name), parentSchema));
}
}
@@ -59,7 +57,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
}
Set matchedInstancePropertyNames = null;
Iterator names = node.fieldNames();
- boolean collectAnnotations = collectAnnotations() || collectAnnotations(executionContext);
+ boolean collectAnnotations = hasUnevaluatedPropertiesInEvaluationPath(executionContext) || collectAnnotations(executionContext);
while (names.hasNext()) {
String name = names.next();
JsonNode n = node.get(name);
@@ -67,7 +65,13 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (entry.getKey().matches(name)) {
NodePath path = instanceLocation.append(name);
int currentErrors = executionContext.getErrors().size();
- entry.getValue().validate(executionContext, n, rootNode, path);
+ Schema schema = entry.getValue();
+ executionContext.evaluationPathAddLast(schema.getSchemaLocation().getFragment().getElement(-1).toString());
+ try {
+ schema.validate(executionContext, n, rootNode, path);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
if (currentErrors == executionContext.getErrors().size()) { // No new errors
if (collectAnnotations) {
if (matchedInstancePropertyNames == null) {
@@ -82,7 +86,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (collectAnnotations) {
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword())
.value(matchedInstancePropertyNames != null ? matchedInstancePropertyNames
: Collections.emptySet())
@@ -90,20 +94,8 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
}
}
- private boolean collectAnnotations() {
- return hasUnevaluatedPropertiesValidator();
- }
-
- private boolean hasUnevaluatedPropertiesValidator() {
- if (this.hasUnevaluatedPropertiesValidator == null) {
- this.hasUnevaluatedPropertiesValidator = hasAdjacentKeywordInEvaluationPath("unevaluatedProperties");
- }
- return hasUnevaluatedPropertiesValidator;
- }
-
@Override
public void preloadSchema() {
preloadSchemas(schemas.values());
- collectAnnotations(); // cache the flag
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/PatternValidator.java b/src/main/java/com/networknt/schema/keyword/PatternValidator.java
index 3af2bb054..b5fcc51b1 100644
--- a/src/main/java/com/networknt/schema/keyword/PatternValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/PatternValidator.java
@@ -38,8 +38,8 @@ public class PatternValidator extends BaseKeywordValidator {
private final String pattern;
private final RegularExpression compiledPattern;
- public PatternValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.PATTERN, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public PatternValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.PATTERN, schemaNode, schemaLocation, parentSchema, schemaContext);
this.pattern = Optional.ofNullable(schemaNode).filter(JsonNode::isTextual).map(JsonNode::textValue).orElse(null);
try {
@@ -67,7 +67,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
try {
if (!matches(node.asText())) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(this.pattern).build());
return;
}
diff --git a/src/main/java/com/networknt/schema/keyword/PrefixItemsValidator.java b/src/main/java/com/networknt/schema/keyword/PrefixItemsValidator.java
index 2db49e477..a81f6c60d 100644
--- a/src/main/java/com/networknt/schema/keyword/PrefixItemsValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/PrefixItemsValidator.java
@@ -36,16 +36,14 @@
public class PrefixItemsValidator extends BaseKeywordValidator {
private final List tupleSchema;
- private Boolean hasUnevaluatedItemsValidator = null;
-
- public PrefixItemsValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.PREFIX_ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public PrefixItemsValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.PREFIX_ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode instanceof ArrayNode && !schemaNode.isEmpty()) {
int i = 0;
this.tupleSchema = new ArrayList<>(schemaNode.size());
for (JsonNode s : schemaNode) {
- this.tupleSchema.add(schemaContext.newSchema(schemaLocation.append(i), evaluationPath.append(i), s,
+ this.tupleSchema.add(schemaContext.newSchema(schemaLocation.append(i), s,
parentSchema));
i++;
}
@@ -62,11 +60,16 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
int count = Math.min(node.size(), this.tupleSchema.size());
for (int i = 0; i < count; ++i) {
NodePath path = instanceLocation.append(i);
- this.tupleSchema.get(i).validate(executionContext, node.get(i), rootNode, path);
+ executionContext.evaluationPathAddLast(i);
+ try {
+ this.tupleSchema.get(i).validate(executionContext, node.get(i), rootNode, path);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
}
// Add annotation
- if (collectAnnotations() || collectAnnotations(executionContext)) {
+ if (hasUnevaluatedItemsInEvaluationPath(executionContext) || collectAnnotations(executionContext)) {
// Tuples
int items = node.isArray() ? node.size() : 1;
int schemas = this.tupleSchema.size();
@@ -74,13 +77,13 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
// More items than schemas so the keyword only applied to the number of schemas
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(schemas).build());
} else {
// Applies to all
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(true).build());
}
}
@@ -95,7 +98,7 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
for (int i = 0; i < count; ++i) {
JsonNode n = node.get(i);
if (executionContext.getWalkConfig().getApplyDefaultsStrategy().shouldApplyArrayDefaults()) {
- JsonNode defaultNode = getDefaultNode(this.tupleSchema.get(i));
+ JsonNode defaultNode = getDefaultNode(this.tupleSchema.get(i), executionContext);
if (n != null) {
// Defaults only set if array index is explicitly null
if (n.isNull() && defaultNode != null) {
@@ -108,7 +111,7 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
}
// Add annotation
- if (collectAnnotations() || collectAnnotations(executionContext)) {
+ if (hasUnevaluatedItemsInEvaluationPath(executionContext) || collectAnnotations(executionContext)) {
// Tuples
int items = node.isArray() ? node.size() : 1;
int schemas = this.tupleSchema.size();
@@ -116,13 +119,13 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
// More items than schemas so the keyword only applied to the number of schemas
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(schemas).build());
} else {
// Applies to all
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(true).build());
}
}
@@ -134,12 +137,12 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
}
}
- private static JsonNode getDefaultNode(Schema schema) {
+ private static JsonNode getDefaultNode(Schema schema, ExecutionContext executionContext) {
JsonNode result = schema.getSchemaNode().get("default");
if (result == null) {
- SchemaRef schemaRef = SchemaRefs.from(schema);
+ SchemaRef schemaRef = SchemaRefs.from(schema, executionContext);
if (schemaRef != null) {
- result = getDefaultNode(schemaRef.getSchema());
+ result = getDefaultNode(schemaRef.getSchema(), executionContext);
}
}
return result;
@@ -147,11 +150,11 @@ private static JsonNode getDefaultNode(Schema schema) {
private void doWalk(ExecutionContext executionContext, int i,
JsonNode node, JsonNode rootNode, NodePath instanceLocation, boolean shouldValidateSchema) {
- walkSchema(executionContext, this.tupleSchema.get(i), node, rootNode, instanceLocation.append(i),
+ walkSchema(executionContext, i, this.tupleSchema.get(i), node, rootNode, instanceLocation.append(i),
shouldValidateSchema);
}
- private void walkSchema(ExecutionContext executionContext, Schema walkSchema, JsonNode node, JsonNode rootNode,
+ private void walkSchema(ExecutionContext executionContext, int schemaIndex, Schema walkSchema, JsonNode node, JsonNode rootNode,
NodePath instanceLocation, boolean shouldValidateSchema) {
//@formatter:off
boolean executeWalk = executionContext.getWalkConfig().getItemWalkListenerRunner().runPreWalkListeners(
@@ -164,7 +167,12 @@ private void walkSchema(ExecutionContext executionContext, Schema walkSchema, Js
);
int currentErrors = executionContext.getErrors().size();
if (executeWalk) {
- walkSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ executionContext.evaluationPathAddLast(schemaIndex);
+ try {
+ walkSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
}
executionContext.getWalkConfig().getItemWalkListenerRunner().runPostWalkListeners(
executionContext,
@@ -182,21 +190,9 @@ public List getTupleSchema() {
return this.tupleSchema;
}
- private boolean collectAnnotations() {
- return hasUnevaluatedItemsValidator();
- }
-
- private boolean hasUnevaluatedItemsValidator() {
- if (this.hasUnevaluatedItemsValidator == null) {
- this.hasUnevaluatedItemsValidator = hasAdjacentKeywordInEvaluationPath("unevaluatedItems");
- }
- return hasUnevaluatedItemsValidator;
- }
-
@Override
public void preloadSchema() {
preloadSchemas(this.tupleSchema);
- collectAnnotations(); // cache the flag
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/PropertiesValidator.java b/src/main/java/com/networknt/schema/keyword/PropertiesValidator.java
index ba92c18fa..884e0e880 100644
--- a/src/main/java/com/networknt/schema/keyword/PropertiesValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/PropertiesValidator.java
@@ -44,15 +44,13 @@ public class PropertiesValidator extends BaseKeywordValidator {
public static final String PROPERTY = "properties";
private final Map schemas = new LinkedHashMap<>();
- private Boolean hasUnevaluatedPropertiesValidator;
-
- public PropertiesValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public PropertiesValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext);
for (Iterator> it = schemaNode.fields(); it.hasNext();) {
Entry entry = it.next();
String pname = entry.getKey();
this.schemas.put(pname, schemaContext.newSchema(schemaLocation.append(pname),
- evaluationPath.append(pname), entry.getValue(), parentSchema));
+ entry.getValue(), parentSchema));
}
}
@@ -64,10 +62,8 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
protected void validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode,
NodePath instanceLocation, boolean walk) {
-
-
Set matchedInstancePropertyNames = null;
- boolean collectAnnotations = collectAnnotations() || collectAnnotations(executionContext);
+ boolean collectAnnotations = hasUnevaluatedPropertiesInEvaluationPath(executionContext) || collectAnnotations(executionContext);
for (Entry entry : this.schemas.entrySet()) {
JsonNode propertyNode = node.get(entry.getKey());
if (propertyNode != null) {
@@ -78,12 +74,17 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
}
matchedInstancePropertyNames.add(entry.getKey());
}
- if (!walk) {
- //validate the child element(s)
- entry.getValue().validate(executionContext, propertyNode, rootNode, path);
- } else {
- // check if walker is enabled. If it is enabled it is upto the walker implementation to decide about the validation.
- walkSchema(executionContext, entry, node, rootNode, instanceLocation, true, executionContext.getWalkConfig().getPropertyWalkListenerRunner());
+ executionContext.evaluationPathAddLast(entry.getKey());
+ try {
+ if (!walk) {
+ //validate the child element(s)
+ entry.getValue().validate(executionContext, propertyNode, rootNode, path);
+ } else {
+ // check if walker is enabled. If it is enabled it is upto the walker implementation to decide about the validation.
+ walkSchema(executionContext, entry, node, rootNode, instanceLocation, true, executionContext.getWalkConfig().getPropertyWalkListenerRunner());
+ }
+ } finally {
+ executionContext.evaluationPathRemoveLast();
}
} else {
if (walk) {
@@ -92,14 +93,20 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
// null.
// The actual walk needs to be skipped as the validators assume that node is not
// null.
- walkSchema(executionContext, entry, node, rootNode, instanceLocation, true, executionContext.getWalkConfig().getPropertyWalkListenerRunner());
+ executionContext.evaluationPathAddLast(entry.getKey());
+ try {
+ walkSchema(executionContext, entry, node, rootNode, instanceLocation, true,
+ executionContext.getWalkConfig().getPropertyWalkListenerRunner());
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
}
}
}
if (collectAnnotations) {
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword(getKeyword()).value(matchedInstancePropertyNames == null ? Collections.emptySet()
: matchedInstancePropertyNames)
.build());
@@ -118,27 +125,21 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
} else {
WalkListenerRunner propertyWalkListenerRunner = executionContext.getWalkConfig().getPropertyWalkListenerRunner();
for (Map.Entry entry : this.schemas.entrySet()) {
- walkSchema(executionContext, entry, node, rootNode, instanceLocation, shouldValidateSchema, propertyWalkListenerRunner);
+ executionContext.evaluationPathAddLast(entry.getKey());
+ try {
+ walkSchema(executionContext, entry, node, rootNode, instanceLocation, shouldValidateSchema, propertyWalkListenerRunner);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
}
}
}
- private boolean collectAnnotations() {
- return hasUnevaluatedPropertiesValidator();
- }
-
- private boolean hasUnevaluatedPropertiesValidator() {
- if (this.hasUnevaluatedPropertiesValidator == null) {
- this.hasUnevaluatedPropertiesValidator = hasAdjacentKeywordInEvaluationPath("unevaluatedProperties");
- }
- return hasUnevaluatedPropertiesValidator;
- }
-
private void applyPropertyDefaults(ObjectNode node, ExecutionContext executionContext) {
for (Map.Entry entry : this.schemas.entrySet()) {
JsonNode propertyNode = node.get(entry.getKey());
- JsonNode defaultNode = getDefaultNode(entry.getValue());
+ JsonNode defaultNode = getDefaultNode(entry.getValue(), executionContext);
if (defaultNode == null) {
continue;
}
@@ -150,12 +151,12 @@ private void applyPropertyDefaults(ObjectNode node, ExecutionContext executionCo
}
}
- private static JsonNode getDefaultNode(Schema schema) {
+ private static JsonNode getDefaultNode(Schema schema, ExecutionContext executionContext) {
JsonNode result = schema.getSchemaNode().get("default");
if (result == null) {
- SchemaRef schemaRef = SchemaRefs.from(schema);
+ SchemaRef schemaRef = SchemaRefs.from(schema, executionContext);
if (schemaRef != null) {
- result = getDefaultNode(schemaRef.getSchema());
+ result = getDefaultNode(schemaRef.getSchema(), executionContext);
}
}
return result;
@@ -189,6 +190,5 @@ public Map getSchemas() {
@Override
public void preloadSchema() {
preloadSchemas(this.schemas.values());
- collectAnnotations(); // cache the flag
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/PropertyDependenciesValidator.java b/src/main/java/com/networknt/schema/keyword/PropertyDependenciesValidator.java
index 4d13cdd42..fc257ec49 100644
--- a/src/main/java/com/networknt/schema/keyword/PropertyDependenciesValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/PropertyDependenciesValidator.java
@@ -37,16 +37,14 @@ public class PropertyDependenciesValidator extends BaseKeywordValidator implemen
*/
private final Map> propertyDependencies;
- public PropertyDependenciesValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public PropertyDependenciesValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.PROPERTY_DEPENDENCIES, schemaNode, schemaLocation, parentSchema, schemaContext,
- evaluationPath);
+ super(KeywordType.PROPERTY_DEPENDENCIES, schemaNode, schemaLocation, parentSchema, schemaContext);
Set> properties = schemaNode.properties();
this.propertyDependencies = new LinkedHashMap<>(properties.size());
for (Entry property : properties) {
String propertyName = property.getKey();
SchemaLocation propertySchemaLocation = schemaLocation.append(propertyName);
- NodePath propertyEvaluationPath = evaluationPath.append(propertyName);
Set> propertyValues = property.getValue().properties();
for (Entry propertyValue : propertyValues) {
@@ -54,7 +52,7 @@ public PropertyDependenciesValidator(SchemaLocation schemaLocation, NodePath eva
key -> new LinkedHashMap<>());
valueSchemas.put(propertyValue.getKey(),
schemaContext.newSchema(propertySchemaLocation.append(propertyValue.getKey()),
- propertyEvaluationPath.append(propertyValue.getKey()), propertyValue.getValue(),
+ propertyValue.getValue(),
parentSchema));
}
}
@@ -75,13 +73,23 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
if (propertyValue != null) {
Map propertySchemas = this.propertyDependencies.get(propertyName);
if (propertySchemas != null) {
- Schema schema = propertySchemas.get(propertyValue);
- if (schema != null) {
- if (!walk) {
- schema.validate(executionContext, node, rootNode, instanceLocation);
- } else {
- schema.walk(executionContext, node, rootNode, instanceLocation, true);
+ executionContext.evaluationPathAddLast(propertyName);
+ try {
+ Schema schema = propertySchemas.get(propertyValue);
+ if (schema != null) {
+ executionContext.evaluationPathAddLast(propertyValue);
+ try {
+ if (!walk) {
+ schema.validate(executionContext, node, rootNode, instanceLocation);
+ } else {
+ schema.walk(executionContext, node, rootNode, instanceLocation, true);
+ }
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
}
+ } finally {
+ executionContext.evaluationPathRemoveLast();
}
}
}
@@ -96,9 +104,20 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
return;
}
- for (Map properties : this.propertyDependencies.values()) {
- for (Schema schema : properties.values()) {
- schema.walk(executionContext, node, rootNode, instanceLocation, false);
+ for (Entry> property : this.propertyDependencies.entrySet()) {
+ String propertyName = property.getKey();
+ executionContext.evaluationPathAddLast(propertyName);
+ try {
+ for (Entry propertyValue : property.getValue().entrySet()) {
+ executionContext.evaluationPathAddLast(propertyValue.getKey());
+ try {
+ propertyValue.getValue().walk(executionContext, node, rootNode, instanceLocation, false);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
+ }
+ } finally {
+ executionContext.evaluationPathRemoveLast();
}
}
}
@@ -106,6 +125,7 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
@Override
public void preloadSchema() {
for (Map properties : propertyDependencies.values()) {
+
for (Schema schema : properties.values()) {
schema.initializeValidators();
}
diff --git a/src/main/java/com/networknt/schema/keyword/PropertyNamesValidator.java b/src/main/java/com/networknt/schema/keyword/PropertyNamesValidator.java
index a3502cee8..df64c28ca 100644
--- a/src/main/java/com/networknt/schema/keyword/PropertyNamesValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/PropertyNamesValidator.java
@@ -30,9 +30,9 @@
public class PropertyNamesValidator extends BaseKeywordValidator implements KeywordValidator {
private final Schema innerSchema;
- public PropertyNamesValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.PROPERTY_NAMES, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
- innerSchema = schemaContext.newSchema(schemaLocation, evaluationPath, schemaNode, parentSchema);
+ public PropertyNamesValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.PROPERTY_NAMES, schemaNode, schemaLocation, parentSchema, schemaContext);
+ innerSchema = schemaContext.newSchema(schemaLocation, schemaNode, parentSchema);
}
public void validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, NodePath instanceLocation) {
@@ -48,7 +48,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
for (final Error schemaError : schemaErrors) {
existingErrors.add(
error().property(pname).instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(pname, schemaError.getMessage()).build());
}
schemaErrors.clear();
diff --git a/src/main/java/com/networknt/schema/keyword/ReadOnlyValidator.java b/src/main/java/com/networknt/schema/keyword/ReadOnlyValidator.java
index 530711df3..cd83d4679 100644
--- a/src/main/java/com/networknt/schema/keyword/ReadOnlyValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/ReadOnlyValidator.java
@@ -32,8 +32,8 @@
public class ReadOnlyValidator extends BaseKeywordValidator {
private static final Logger logger = LoggerFactory.getLogger(ReadOnlyValidator.class);
- public ReadOnlyValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.READ_ONLY, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public ReadOnlyValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.READ_ONLY, schemaNode, schemaLocation, parentSchema, schemaContext);
logger.debug("Loaded ReadOnlyValidator for property {} as {}", parentSchema, "read mode");
}
@@ -42,7 +42,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (Boolean.TRUE.equals(executionContext.getExecutionConfig().getReadOnly())) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.build());
}
return;
diff --git a/src/main/java/com/networknt/schema/keyword/RecursiveRefValidator.java b/src/main/java/com/networknt/schema/keyword/RecursiveRefValidator.java
index e864d3aef..158528755 100644
--- a/src/main/java/com/networknt/schema/keyword/RecursiveRefValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/RecursiveRefValidator.java
@@ -24,20 +24,18 @@
import com.networknt.schema.SchemaException;
import com.networknt.schema.SchemaRef;
import com.networknt.schema.path.NodePath;
-import com.networknt.schema.utils.ThreadSafeCachingSupplier;
import com.networknt.schema.SchemaLocation;
import com.networknt.schema.SchemaContext;
-import java.util.function.Supplier;
+import java.util.Iterator;
/**
* {@link KeywordValidator} that resolves $recursiveRef.
*/
public class RecursiveRefValidator extends BaseKeywordValidator {
- protected final SchemaRef schema;
- public RecursiveRefValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.RECURSIVE_REF, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public RecursiveRefValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.RECURSIVE_REF, schemaNode, schemaLocation, parentSchema, schemaContext);
String refValue = schemaNode.asText();
if (!"#".equals(refValue)) {
@@ -45,34 +43,23 @@ public RecursiveRefValidator(SchemaLocation schemaLocation, NodePath evaluationP
.keyword(KeywordType.RECURSIVE_REF.getValue()).messageKey("internal.invalidRecursiveRef")
.message("The value of a $recursiveRef must be '#' but is '{0}'").instanceLocation(schemaLocation.getFragment())
.instanceNode(this.schemaNode)
- .evaluationPath(evaluationPath).arguments(refValue).build();
+ .arguments(refValue).build();
throw new SchemaException(error);
}
- this.schema = getRefSchema(parentSchema, schemaContext, refValue, evaluationPath);
}
- static SchemaRef getRefSchema(Schema parentSchema, SchemaContext schemaContext, String refValue,
- NodePath evaluationPath) {
- return new SchemaRef(getSupplier(() -> getSchema(parentSchema, schemaContext, refValue, evaluationPath), schemaContext.getSchemaRegistryConfig().isCacheRefs()));
- }
-
- static Supplier getSupplier(Supplier supplier, boolean cache) {
- return cache ? new ThreadSafeCachingSupplier<>(supplier) : supplier;
- }
-
- static Schema getSchema(Schema parentSchema, SchemaContext schemaContext, String refValue,
- NodePath evaluationPath) {
+ static Schema getSchema(Schema parentSchema, ExecutionContext executionContext) {
Schema refSchema = parentSchema.findSchemaResourceRoot(); // Get the document
Schema current = refSchema;
Schema check = null;
String base = null;
String baseCheck = null;
- if (refSchema != null)
+ if (refSchema != null) {
base = current.getSchemaLocation().getAbsoluteIri() != null ? current.getSchemaLocation().getAbsoluteIri().toString() : "";
if (current.isRecursiveAnchor()) {
// Check dynamic scope
- while (current.getEvaluationParentSchema() != null) {
- current = current.getEvaluationParentSchema();
+ for (Iterator iter = executionContext.getEvaluationSchema().descendingIterator(); iter.hasNext();) {
+ current = iter.next();
baseCheck = current.getSchemaLocation().getAbsoluteIri() != null ? current.getSchemaLocation().getAbsoluteIri().toString() : "";
if (!base.equals(baseCheck)) {
base = baseCheck;
@@ -84,20 +71,17 @@ static Schema getSchema(Schema parentSchema, SchemaContext schemaContext, String
}
}
}
- if (refSchema != null) {
- refSchema = refSchema.fromRef(parentSchema, evaluationPath);
}
return refSchema;
}
@Override
public void validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, NodePath instanceLocation) {
-
- Schema refSchema = this.schema.getSchema();
+ Schema refSchema = getSchemaRef(executionContext).getSchema();
if (refSchema == null) {
Error error = error().keyword(KeywordType.RECURSIVE_REF.getValue())
.messageKey("internal.unresolvedRef").message("Reference {0} cannot be resolved")
- .instanceLocation(instanceLocation).evaluationPath(getEvaluationPath())
+ .instanceLocation(instanceLocation).evaluationPath(executionContext.getEvaluationPath())
.arguments(schemaNode.asText()).build();
throw new InvalidSchemaRefException(error);
}
@@ -110,21 +94,20 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
// This is important because if we use same JsonSchemaFactory for creating multiple JSONSchema instances,
// these schemas will be cached along with config. We have to replace the config for cached $ref references
// with the latest config. Reset the config.
- Schema refSchema = this.schema.getSchema();
+ Schema refSchema = getSchemaRef(executionContext).getSchema();
if (refSchema == null) {
Error error = error().keyword(KeywordType.RECURSIVE_REF.getValue())
.messageKey("internal.unresolvedRef").message("Reference {0} cannot be resolved")
- .instanceLocation(instanceLocation).evaluationPath(getEvaluationPath())
+ .instanceLocation(instanceLocation).evaluationPath(executionContext.getEvaluationPath())
.arguments(schemaNode.asText()).build();
throw new InvalidSchemaRefException(error);
}
if (node == null) {
// Check for circular dependency
- SchemaLocation schemaLocation = refSchema.getSchemaLocation();
- Schema check = refSchema;
boolean circularDependency = false;
- while (check.getEvaluationParentSchema() != null) {
- check = check.getEvaluationParentSchema();
+ SchemaLocation schemaLocation = refSchema.getSchemaLocation();
+ for (Iterator iter = executionContext.getEvaluationSchema().descendingIterator(); iter.hasNext();) {
+ Schema check = iter.next();
if (check.getSchemaLocation().equals(schemaLocation)) {
circularDependency = true;
break;
@@ -137,39 +120,7 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
refSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
}
- public SchemaRef getSchemaRef() {
- return this.schema;
- }
-
- @Override
- public void preloadSchema() {
- Schema jsonSchema = null;
- try {
- jsonSchema = this.schema.getSchema();
- } catch (SchemaException e) {
- throw e;
- } catch (RuntimeException e) {
- throw new SchemaException(e);
- }
- // Check for circular dependency
- // Only one cycle is pre-loaded
- // The rest of the cycles will load at execution time depending on the input
- // data
- SchemaLocation schemaLocation = jsonSchema.getSchemaLocation();
- Schema check = jsonSchema;
- boolean circularDependency = false;
- int depth = 0;
- while (check.getEvaluationParentSchema() != null) {
- depth++;
- check = check.getEvaluationParentSchema();
- if (check.getSchemaLocation().equals(schemaLocation)) {
- circularDependency = true;
- break;
- }
- }
- if (this.schemaContext.getSchemaRegistryConfig().isCacheRefs() && !circularDependency
- && depth < this.schemaContext.getSchemaRegistryConfig().getPreloadSchemaRefMaxNestingDepth()) {
- jsonSchema.initializeValidators();
- }
+ public SchemaRef getSchemaRef(ExecutionContext executionContext) {
+ return new SchemaRef(() -> getSchema(this.parentSchema, executionContext));
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/RefValidator.java b/src/main/java/com/networknt/schema/keyword/RefValidator.java
index 105744cb8..4ea868615 100644
--- a/src/main/java/com/networknt/schema/keyword/RefValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/RefValidator.java
@@ -28,6 +28,7 @@
import com.networknt.schema.SchemaLocation;
import com.networknt.schema.SchemaContext;
+import java.util.Iterator;
import java.util.function.Supplier;
/**
@@ -38,14 +39,13 @@ public class RefValidator extends BaseKeywordValidator {
private static final String REF_CURRENT = "#";
- public RefValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.REF, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public RefValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.REF, schemaNode, schemaLocation, parentSchema, schemaContext);
String refValue = schemaNode.asText();
- this.schema = getRefSchema(parentSchema, schemaContext, refValue, evaluationPath);
+ this.schema = getRefSchema(parentSchema, schemaContext, refValue);
}
- static SchemaRef getRefSchema(Schema parentSchema, SchemaContext schemaContext, String refValue,
- NodePath evaluationPath) {
+ static SchemaRef getRefSchema(Schema parentSchema, SchemaContext schemaContext, String refValue) {
// The evaluationPath is used to derive the keywordLocation
final String refValueOriginal = refValue;
@@ -76,7 +76,7 @@ static SchemaRef getRefSchema(Schema parentSchema, SchemaContext schemaContext,
if (schemaResource == null) {
return null;
}
- return schemaResource.fromRef(parentSchema, evaluationPath);
+ return schemaResource;
} else {
String newRefValue = refValue.substring(index);
String find = schemaLocation.getAbsoluteIri() + newRefValue;
@@ -87,13 +87,13 @@ static SchemaRef getRefSchema(Schema parentSchema, SchemaContext schemaContext,
if (findSchemaResource != null) {
schemaResource = findSchemaResource;
} else {
- schemaResource = getSchema(schemaResource, schemaContext, newRefValue, refValueOriginal,
- evaluationPath);
+ schemaResource = getSchema(schemaResource, schemaContext, newRefValue, refValueOriginal
+ );
}
if (schemaResource == null) {
return null;
}
- return schemaResource.fromRef(parentSchema, evaluationPath);
+ return schemaResource;
}
}, schemaContext.getSchemaRegistryConfig().isCacheRefs()));
@@ -106,22 +106,21 @@ static SchemaRef getRefSchema(Schema parentSchema, SchemaContext schemaContext,
schemaResource = schemaContext.getDynamicAnchors().get(absoluteIri);
}
if (schemaResource == null) {
- schemaResource = getSchema(parentSchema, schemaContext, refValue, refValueOriginal, evaluationPath);
+ schemaResource = getSchema(parentSchema, schemaContext, refValue, refValueOriginal);
}
if (schemaResource == null) {
return null;
}
- return schemaResource.fromRef(parentSchema, evaluationPath);
+ return schemaResource;
}, schemaContext.getSchemaRegistryConfig().isCacheRefs()));
}
if (refValue.equals(REF_CURRENT)) {
return new SchemaRef(
- getSupplier(() -> parentSchema.findSchemaResourceRoot().fromRef(parentSchema, evaluationPath),
+ getSupplier(() -> parentSchema.findSchemaResourceRoot(),
schemaContext.getSchemaRegistryConfig().isCacheRefs()));
}
return new SchemaRef(getSupplier(
- () -> getSchema(parentSchema, schemaContext, refValue, refValueOriginal, evaluationPath)
- .fromRef(parentSchema, evaluationPath),
+ () -> getSchema(parentSchema, schemaContext, refValue, refValueOriginal),
schemaContext.getSchemaRegistryConfig().isCacheRefs()));
}
@@ -156,16 +155,16 @@ private static String resolve(Schema parentSchema, String refValue) {
private static Schema getSchema(Schema parent,
SchemaContext schemaContext,
String refValue,
- String refValueOriginal,
- NodePath evaluationPath) {
- // This should be processing json pointer fragments only
- NodePath fragment = SchemaLocation.Fragment.of(refValue);
+ String refValueOriginal
+ ) {
String schemaReference = resolve(parent, refValueOriginal);
// ConcurrentHashMap computeIfAbsent does not allow calls that result in a
// recursive update to the map.
// The getSubSchema potentially recurses to call back to getJsonSchema again
Schema result = schemaContext.getSchemaReferences().get(schemaReference);
if (result == null) {
+ // This should be processing json pointer fragments only
+ NodePath fragment = SchemaLocation.Fragment.of(refValue);
synchronized (schemaContext.getSchemaRegistry()) { // acquire lock on shared factory object to prevent deadlock
result = schemaContext.getSchemaReferences().get(schemaReference);
if (result == null) {
@@ -186,7 +185,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (refSchema == null) {
Error error = error().keyword(KeywordType.REF.getValue())
.messageKey("internal.unresolvedRef").message("Reference {0} cannot be resolved")
- .instanceLocation(instanceLocation).evaluationPath(getEvaluationPath())
+ .instanceLocation(instanceLocation).evaluationPath(executionContext.getEvaluationPath())
.arguments(schemaNode.asText()).build();
throw new InvalidSchemaRefException(error);
}
@@ -203,17 +202,16 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
if (refSchema == null) {
Error error = error().keyword(KeywordType.REF.getValue())
.messageKey("internal.unresolvedRef").message("Reference {0} cannot be resolved")
- .instanceLocation(instanceLocation).evaluationPath(getEvaluationPath())
+ .instanceLocation(instanceLocation).evaluationPath(executionContext.getEvaluationPath())
.arguments(schemaNode.asText()).build();
throw new InvalidSchemaRefException(error);
}
if (node == null) {
// Check for circular dependency
- SchemaLocation schemaLocation = refSchema.getSchemaLocation();
- Schema check = refSchema;
boolean circularDependency = false;
- while (check.getEvaluationParentSchema() != null) {
- check = check.getEvaluationParentSchema();
+ SchemaLocation schemaLocation = refSchema.getSchemaLocation();
+ for (Iterator iter = executionContext.getEvaluationSchema().descendingIterator(); iter.hasNext();) {
+ Schema check = iter.next();
if (check.getSchemaLocation().equals(schemaLocation)) {
circularDependency = true;
break;
@@ -240,10 +238,12 @@ public void preloadSchema() {
} catch (RuntimeException e) {
throw new SchemaException(e);
}
+ jsonSchema.initializeValidators();
// Check for circular dependency
// Only one cycle is pre-loaded
// The rest of the cycles will load at execution time depending on the input
// data
+ /*
SchemaLocation schemaLocation = jsonSchema.getSchemaLocation();
Schema check = jsonSchema;
boolean circularDependency = false;
@@ -260,5 +260,6 @@ public void preloadSchema() {
&& depth < this.schemaContext.getSchemaRegistryConfig().getPreloadSchemaRefMaxNestingDepth()) {
jsonSchema.initializeValidators();
}
+ */
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/RequiredValidator.java b/src/main/java/com/networknt/schema/keyword/RequiredValidator.java
index ca5c337b8..7f4dde24e 100644
--- a/src/main/java/com/networknt/schema/keyword/RequiredValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/RequiredValidator.java
@@ -31,8 +31,8 @@
public class RequiredValidator extends BaseKeywordValidator implements KeywordValidator {
private final List fieldNames;
- public RequiredValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.REQUIRED, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public RequiredValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.REQUIRED, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode.isArray()) {
this.fieldNames = new ArrayList<>(schemaNode.size());
for (JsonNode fieldNme : schemaNode) {
@@ -73,7 +73,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
* @see Basic
*/
executionContext.addError(error().instanceNode(node).property(fieldName).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(fieldName).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/TrueValidator.java b/src/main/java/com/networknt/schema/keyword/TrueValidator.java
index 71347de9d..fad0d0573 100644
--- a/src/main/java/com/networknt/schema/keyword/TrueValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/TrueValidator.java
@@ -26,8 +26,8 @@
* {@link KeywordValidator} for true.
*/
public class TrueValidator extends BaseKeywordValidator implements KeywordValidator {
- public TrueValidator(SchemaLocation schemaLocation, NodePath evaluationPath, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.TRUE, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public TrueValidator(SchemaLocation schemaLocation, final JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.TRUE, schemaNode, schemaLocation, parentSchema, schemaContext);
}
public void validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, NodePath instanceLocation) {
diff --git a/src/main/java/com/networknt/schema/keyword/TypeValidator.java b/src/main/java/com/networknt/schema/keyword/TypeValidator.java
index 0c67413c6..70abe92e6 100644
--- a/src/main/java/com/networknt/schema/keyword/TypeValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/TypeValidator.java
@@ -33,11 +33,11 @@ public class TypeValidator extends BaseKeywordValidator {
private final JsonType schemaType;
private final UnionTypeValidator unionTypeValidator;
- public TypeValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.TYPE, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public TypeValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.TYPE, schemaNode, schemaLocation, parentSchema, schemaContext);
this.schemaType = TypeFactory.getSchemaNodeType(schemaNode);
if (this.schemaType == JsonType.UNION) {
- this.unionTypeValidator = new UnionTypeValidator(schemaLocation, evaluationPath, schemaNode, parentSchema, schemaContext);
+ this.unionTypeValidator = new UnionTypeValidator(schemaLocation, schemaNode, parentSchema, schemaContext);
} else {
this.unionTypeValidator = null;
}
@@ -47,8 +47,8 @@ public JsonType getSchemaType() {
return this.schemaType;
}
- public boolean equalsToSchemaType(JsonNode node) {
- return JsonNodeTypes.equalsToSchemaType(node, this.schemaType, this.parentSchema, this.schemaContext);
+ public boolean equalsToSchemaType(JsonNode node, ExecutionContext executionContext) {
+ return JsonNodeTypes.equalsToSchemaType(node, this.schemaType, this.parentSchema, this.schemaContext, executionContext);
}
@Override
@@ -60,10 +60,10 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
return;
}
- if (!equalsToSchemaType(node)) {
+ if (!equalsToSchemaType(node, executionContext)) {
JsonType nodeType = TypeFactory.getValueNodeType(node, this.schemaContext.getSchemaRegistryConfig());
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(nodeType.toString(), this.schemaType.toString()).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/UnevaluatedItemsValidator.java b/src/main/java/com/networknt/schema/keyword/UnevaluatedItemsValidator.java
index f596ad7f9..ddeedd319 100644
--- a/src/main/java/com/networknt/schema/keyword/UnevaluatedItemsValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/UnevaluatedItemsValidator.java
@@ -38,13 +38,12 @@ public class UnevaluatedItemsValidator extends BaseKeywordValidator {
private final boolean isMinV202012;
- public UnevaluatedItemsValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public UnevaluatedItemsValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.UNEVALUATED_ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext,
- evaluationPath);
+ super(KeywordType.UNEVALUATED_ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext);
isMinV202012 = MIN_DRAFT_2020_12.getVersions().contains(schemaContext.getDialect().getSpecificationVersion());
if (schemaNode.isObject() || schemaNode.isBoolean()) {
- this.schema = schemaContext.newSchema(schemaLocation, evaluationPath, schemaNode, parentSchema);
+ this.schema = schemaContext.newSchema(schemaLocation, schemaNode, parentSchema);
} else {
throw new IllegalArgumentException("The value of 'unevaluatedItems' MUST be a valid JSON Schema.");
}
@@ -73,10 +72,11 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
boolean evaluated = false;
// Get all the valid adjacent annotations
- Predicate validEvaluationPathFilter = a -> executionContext.getInstanceResults().isValid(instanceLocation, a.getEvaluationPath());
+ Predicate validEvaluationPathFilter = a -> a.isValid();
+ //Predicate validEvaluationPathFilter = a -> executionContext.getInstanceResults().isValid(instanceLocation, a.getEvaluationPath());
Predicate adjacentEvaluationPathFilter = a -> a.getEvaluationPath()
- .startsWith(this.evaluationPath.getParent());
+ .startsWith(executionContext.getEvaluationPath().getParent());
List instanceLocationAnnotations = executionContext.getAnnotations().asMap()
.getOrDefault(instanceLocation, Collections.emptyList());
@@ -173,7 +173,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (this.schemaNode.isBoolean() && this.schemaNode.booleanValue() == false) {
// All fails as "unevaluatedItems: false"
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation).arguments(x)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.build());
} else {
// Schema errors will be reported as is
@@ -196,7 +196,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (evaluated) {
executionContext.getAnnotations()
.put(Annotation.builder().instanceLocation(instanceLocation)
- .evaluationPath(this.evaluationPath).schemaLocation(this.schemaLocation)
+ .evaluationPath(executionContext.getEvaluationPath()).schemaLocation(this.schemaLocation)
.keyword("unevaluatedItems").value(true).build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/UnevaluatedPropertiesValidator.java b/src/main/java/com/networknt/schema/keyword/UnevaluatedPropertiesValidator.java
index fbdfa4dee..f92f15dd5 100644
--- a/src/main/java/com/networknt/schema/keyword/UnevaluatedPropertiesValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/UnevaluatedPropertiesValidator.java
@@ -38,11 +38,11 @@
public class UnevaluatedPropertiesValidator extends BaseKeywordValidator {
private final Schema schema;
- public UnevaluatedPropertiesValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.UNEVALUATED_PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public UnevaluatedPropertiesValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.UNEVALUATED_PROPERTIES, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode.isObject() || schemaNode.isBoolean()) {
- this.schema = schemaContext.newSchema(schemaLocation, evaluationPath, schemaNode, parentSchema);
+ this.schema = schemaContext.newSchema(schemaLocation, schemaNode, parentSchema);
} else {
throw new IllegalArgumentException("The value of 'unevaluatedProperties' MUST be a valid JSON Schema.");
}
@@ -56,10 +56,11 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
// Get all the valid adjacent annotations
- Predicate validEvaluationPathFilter = a -> executionContext.getInstanceResults().isValid(instanceLocation, a.getEvaluationPath());
+ Predicate validEvaluationPathFilter = a -> a.isValid();
+ //Predicate validEvaluationPathFilter = a -> executionContext.getInstanceResults().isValid(instanceLocation, a.getEvaluationPath());
Predicate adjacentEvaluationPathFilter = a -> a.getEvaluationPath()
- .startsWith(this.evaluationPath.getParent());
+ .startsWith(executionContext.getEvaluationPath().getParent());
List instanceLocationAnnotations = executionContext.getAnnotations().asMap()
.getOrDefault(instanceLocation, Collections.emptyList());
@@ -121,7 +122,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (this.schemaNode.isBoolean() && this.schemaNode.booleanValue() == false) {
// All fails as "unevaluatedProperties: false"
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation).property(fieldName)
- .arguments(fieldName).locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).arguments(fieldName).locale(executionContext.getExecutionConfig().getLocale())
.build());
} else {
// Schema errors will be reported as is
@@ -134,7 +135,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
executionContext.setFailFast(failFast); // restore flag
}
executionContext.getAnnotations()
- .put(Annotation.builder().instanceLocation(instanceLocation).evaluationPath(this.evaluationPath)
+ .put(Annotation.builder().instanceLocation(instanceLocation).evaluationPath(executionContext.getEvaluationPath())
.schemaLocation(this.schemaLocation).keyword(getKeyword()).value(evaluatedProperties).build());
return;
diff --git a/src/main/java/com/networknt/schema/keyword/UnionTypeValidator.java b/src/main/java/com/networknt/schema/keyword/UnionTypeValidator.java
index c42b3018b..be4f29429 100644
--- a/src/main/java/com/networknt/schema/keyword/UnionTypeValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/UnionTypeValidator.java
@@ -38,8 +38,8 @@ public class UnionTypeValidator extends BaseKeywordValidator implements KeywordV
private final List schemas;
private final String error;
- public UnionTypeValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.TYPE, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public UnionTypeValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.TYPE, schemaNode, schemaLocation, parentSchema, schemaContext);
StringBuilder errorBuilder = new StringBuilder();
String sep = "";
@@ -57,11 +57,9 @@ public UnionTypeValidator(SchemaLocation schemaLocation, NodePath evaluationPath
sep = ", ";
if (n.isObject()) {
- schemas.add(schemaContext.newSchema(schemaLocation.append(KeywordType.TYPE.getValue()),
- evaluationPath.append(KeywordType.TRUE.getValue()), n, parentSchema));
+ schemas.add(schemaContext.newSchema(schemaLocation.append(i), n, parentSchema));
} else {
- schemas.add(new TypeValidator(schemaLocation.append(i), evaluationPath.append(i), n, parentSchema,
- schemaContext));
+ schemas.add(new TypeValidator(schemaLocation.append(i), n, parentSchema, schemaContext));
}
i++;
}
@@ -85,8 +83,15 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
List test = new ArrayList<>();
executionContext.setFailFast(false);
executionContext.setErrors(test);
+ int schemaIndex = 0;
for (Validator schema : schemas) {
- schema.validate(executionContext, node, rootNode, instanceLocation);
+ executionContext.evaluationPathAddLast(schemaIndex);
+ try {
+ schema.validate(executionContext, node, rootNode, instanceLocation);
+ } finally {
+ executionContext.evaluationPathRemoveLast();
+ }
+ schemaIndex++;
if (test.isEmpty()) {
valid = true;
break;
@@ -103,7 +108,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (!valid) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
.keyword("type")
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.arguments(nodeType.toString(), error)
.build());
}
diff --git a/src/main/java/com/networknt/schema/keyword/UniqueItemsValidator.java b/src/main/java/com/networknt/schema/keyword/UniqueItemsValidator.java
index 5ee628164..05f5bc080 100644
--- a/src/main/java/com/networknt/schema/keyword/UniqueItemsValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/UniqueItemsValidator.java
@@ -32,8 +32,8 @@
public class UniqueItemsValidator extends BaseKeywordValidator implements KeywordValidator {
private final boolean unique;
- public UniqueItemsValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.UNIQUE_ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public UniqueItemsValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.UNIQUE_ITEMS, schemaNode, schemaLocation, parentSchema, schemaContext);
if (schemaNode.isBoolean()) {
unique = schemaNode.booleanValue();
} else {
@@ -49,7 +49,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
for (JsonNode n : node) {
if (!set.add(n)) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.build());
}
}
diff --git a/src/main/java/com/networknt/schema/keyword/WriteOnlyValidator.java b/src/main/java/com/networknt/schema/keyword/WriteOnlyValidator.java
index e7bc5db9c..3537395d6 100644
--- a/src/main/java/com/networknt/schema/keyword/WriteOnlyValidator.java
+++ b/src/main/java/com/networknt/schema/keyword/WriteOnlyValidator.java
@@ -16,8 +16,8 @@
public class WriteOnlyValidator extends BaseKeywordValidator {
private static final Logger logger = LoggerFactory.getLogger(WriteOnlyValidator.class);
- public WriteOnlyValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
- super(KeywordType.WRITE_ONLY, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ public WriteOnlyValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ super(KeywordType.WRITE_ONLY, schemaNode, schemaLocation, parentSchema, schemaContext);
logger.debug("Loaded WriteOnlyValidator for property {} as {}", parentSchema, "write mode");
}
@@ -26,7 +26,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (Boolean.TRUE.equals(executionContext.getExecutionConfig().getWriteOnly())) {
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
+ .evaluationPath(executionContext.getEvaluationPath()).locale(executionContext.getExecutionConfig().getLocale())
.build());
}
}
diff --git a/src/main/java/com/networknt/schema/output/OutputUnitData.java b/src/main/java/com/networknt/schema/output/OutputUnitData.java
index a2464e9cb..b76e8ded4 100644
--- a/src/main/java/com/networknt/schema/output/OutputUnitData.java
+++ b/src/main/java/com/networknt/schema/output/OutputUnitData.java
@@ -100,8 +100,9 @@ public static OutputUnitData from(List validationErrors, ExecutionContext
OutputUnitKey key = new OutputUnitKey(annotation.getEvaluationPath().getParent(),
annotationSchemaLocation, annotation.getInstanceLocation());
- boolean validResult = executionContext.getInstanceResults().isValid(annotation.getInstanceLocation(),
- annotation.getEvaluationPath());
+// boolean validResult = executionContext.getInstanceResults().isValid(annotation.getInstanceLocation(),
+// annotation.getEvaluationPath());
+ boolean validResult = annotation.isValid();
valid.put(key, validResult);
if (validResult) {
// annotations
diff --git a/src/main/java/com/networknt/schema/path/NodePath.java b/src/main/java/com/networknt/schema/path/NodePath.java
index 8e4cd118d..bb0d953d2 100644
--- a/src/main/java/com/networknt/schema/path/NodePath.java
+++ b/src/main/java/com/networknt/schema/path/NodePath.java
@@ -21,7 +21,7 @@
/**
* Represents a path to a JSON node.
*/
-public class NodePath implements Comparable {
+public class NodePath implements Comparable, Path {
private final PathType type;
private final NodePath parent;
diff --git a/src/main/java/com/networknt/schema/path/Path.java b/src/main/java/com/networknt/schema/path/Path.java
new file mode 100644
index 000000000..c72cc8393
--- /dev/null
+++ b/src/main/java/com/networknt/schema/path/Path.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.networknt.schema.path;
+
+/**
+ * Represents a path.
+ */
+public interface Path {
+ String toString();
+}
diff --git a/src/main/java/com/networknt/schema/utils/JsonNodeTypes.java b/src/main/java/com/networknt/schema/utils/JsonNodeTypes.java
index cdd9bd818..9062aa8e1 100644
--- a/src/main/java/com/networknt/schema/utils/JsonNodeTypes.java
+++ b/src/main/java/com/networknt/schema/utils/JsonNodeTypes.java
@@ -1,6 +1,7 @@
package com.networknt.schema.utils;
import com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.ExecutionContext;
import com.networknt.schema.Schema;
import com.networknt.schema.SchemaContext;
import com.networknt.schema.SchemaRegistryConfig;
@@ -16,10 +17,10 @@ public class JsonNodeTypes {
public static boolean isNodeNullable(JsonNode schema){
JsonNode nullable = schema.get(NULLABLE);
- return nullable != null && nullable.asBoolean();
+ return nullable != null && nullable.asBoolean();
}
- public static boolean equalsToSchemaType(JsonNode node, JsonType schemaType, Schema parentSchema, SchemaContext schemaContext) {
+ public static boolean equalsToSchemaType(JsonNode node, JsonType schemaType, Schema parentSchema, SchemaContext schemaContext, ExecutionContext executionContext) {
SchemaRegistryConfig config = schemaContext.getSchemaRegistryConfig();
JsonType nodeType = TypeFactory.getValueNodeType(node, config);
// in the case that node type is not the same as schema type, try to convert node to the
@@ -49,21 +50,21 @@ public static boolean equalsToSchemaType(JsonNode node, JsonType schemaType, Sch
// Skip the type validation when the schema is an enum object schema. Since the current type
// of node itself can be used for type validation.
- if (isEnumObjectSchema(parentSchema) && !config.isStrict("type", Boolean.TRUE)) {
+ if (!config.isStrict("type", Boolean.TRUE) && isEnumObjectSchema(parentSchema, executionContext)) {
return true;
}
- if (config != null && config.isTypeLoose()) {
+ if (config.isTypeLoose()) {
// if typeLoose is true, everything can be a size 1 array
if (schemaType == JsonType.ARRAY) {
return true;
}
if (nodeType == JsonType.STRING) {
if (schemaType == JsonType.INTEGER) {
- return Strings.isInteger(node.textValue());
+ return Strings.isInteger(node.textValue());
} else if (schemaType == JsonType.BOOLEAN) {
- return Strings.isBoolean(node.textValue());
+ return Strings.isBoolean(node.textValue());
} else if (schemaType == JsonType.NUMBER) {
- return Strings.isNumeric(node.textValue());
+ return Strings.isNumeric(node.textValue());
}
}
}
@@ -96,7 +97,8 @@ public static boolean isNumber(JsonNode node, SchemaRegistryConfig config) {
return false;
}
- private static boolean isEnumObjectSchema(Schema jsonSchema) {
+ private static boolean isEnumObjectSchema(Schema jsonSchema, ExecutionContext executionContext) {
+
// There are three conditions for enum object schema
// 1. The current schema contains key "type", and the value is object
// 2. The current schema contains key "enum", and the value is an array
@@ -110,7 +112,7 @@ private static boolean isEnumObjectSchema(Schema jsonSchema) {
typeNode = jsonSchema.getSchemaNode().get(TYPE);
enumNode = jsonSchema.getSchemaNode().get(ENUM);
}
- refNode = REF.equals(jsonSchema.getEvaluationPath().getElement(-1));
+ refNode = REF.equals(executionContext.getEvaluationPath().getParent().getElement(-1));
}
if (typeNode != null && enumNode != null && refNode) {
return TypeFactory.getSchemaNodeType(typeNode) == JsonType.OBJECT && enumNode.isArray();
diff --git a/src/main/java/com/networknt/schema/utils/SchemaRefs.java b/src/main/java/com/networknt/schema/utils/SchemaRefs.java
index fa8d8ff14..e6782f55e 100644
--- a/src/main/java/com/networknt/schema/utils/SchemaRefs.java
+++ b/src/main/java/com/networknt/schema/utils/SchemaRefs.java
@@ -15,6 +15,7 @@
*/
package com.networknt.schema.utils;
+import com.networknt.schema.ExecutionContext;
import com.networknt.schema.Schema;
import com.networknt.schema.SchemaRef;
import com.networknt.schema.keyword.DynamicRefValidator;
@@ -33,14 +34,14 @@ public class SchemaRefs {
* @param schema the schema
* @return the ref
*/
- public static SchemaRef from(Schema schema) {
+ public static SchemaRef from(Schema schema, ExecutionContext executionContext) {
for (KeywordValidator validator : schema.getValidators()) {
if (validator instanceof RefValidator) {
return ((RefValidator) validator).getSchemaRef();
} else if (validator instanceof DynamicRefValidator) {
- return ((DynamicRefValidator) validator).getSchemaRef();
+ return ((DynamicRefValidator) validator).getSchemaRef(executionContext);
} else if (validator instanceof RecursiveRefValidator) {
- return ((RecursiveRefValidator) validator).getSchemaRef();
+ return ((RecursiveRefValidator) validator).getSchemaRef(executionContext);
}
}
return null;
diff --git a/src/main/java/com/networknt/schema/utils/Strings.java b/src/main/java/com/networknt/schema/utils/Strings.java
index 0858655a3..d16f7202e 100644
--- a/src/main/java/com/networknt/schema/utils/Strings.java
+++ b/src/main/java/com/networknt/schema/utils/Strings.java
@@ -16,6 +16,9 @@
package com.networknt.schema.utils;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Utility methods for working with Strings.
*/
@@ -126,4 +129,44 @@ public static boolean isNumeric(String string) {
public static boolean isBlank(String string) {
return null == string || string.trim().isEmpty();
}
+
+ /**
+ * Split text. Unlike the JDK String split using regex trailing delimiters are
+ * preserved.
+ *
+ * @param text the text to split
+ * @param delimiter the delimiter
+ * @return the fragments
+ */
+ public static String[] split(String text, char delimiter) {
+ if (text == null) {
+ return new String[0];
+ }
+ if (text.isEmpty()) {
+ return new String[]{""};
+ }
+
+ List segments = new ArrayList<>();
+ int start = 0;
+ int end;
+
+ while (start <= text.length()) {
+ end = text.indexOf(delimiter, start);
+
+ if (end == -1) {
+ end = text.length();
+ }
+
+ String segment = text.substring(start, end);
+ segments.add(segment);
+
+ if (end == text.length()) {
+ break;
+ }
+
+ start = end + 1;
+ }
+
+ return segments.toArray(new String[segments.size()]);
+ }
}
diff --git a/src/main/java/com/networknt/schema/utils/TypeFactory.java b/src/main/java/com/networknt/schema/utils/TypeFactory.java
index ec2808c0c..ffb1c4f35 100644
--- a/src/main/java/com/networknt/schema/utils/TypeFactory.java
+++ b/src/main/java/com/networknt/schema/utils/TypeFactory.java
@@ -93,8 +93,7 @@ public static JsonType getValueNodeType(JsonNode node, SchemaRegistryConfig conf
case NUMBER:
if (node.isIntegralNumber()) {
return JsonType.INTEGER;
- } else if (config != null && (config.isJavaSemantics() || config.isLosslessNarrowing())
- && node.canConvertToExactIntegral()) {
+ } else if (config != null && config.isLosslessNarrowing() && node.canConvertToExactIntegral()) {
return JsonType.INTEGER;
} else {
return JsonType.NUMBER;
diff --git a/src/main/java/com/networknt/schema/walk/WalkEvent.java b/src/main/java/com/networknt/schema/walk/WalkEvent.java
index 93549d4fe..cdce63b28 100644
--- a/src/main/java/com/networknt/schema/walk/WalkEvent.java
+++ b/src/main/java/com/networknt/schema/walk/WalkEvent.java
@@ -18,6 +18,11 @@ public class WalkEvent {
private JsonNode instanceNode;
private NodePath instanceLocation;
private KeywordValidator validator;
+ private NodePath evaluationPath;
+
+ public NodePath getEvaluationPath() {
+ return this.evaluationPath;
+ }
/**
* Gets the execution context.
@@ -93,7 +98,7 @@ public T getValidator() {
@Override
public String toString() {
- return "WalkEvent [evaluationPath=" + getSchema().getEvaluationPath() + ", schemaLocation="
+ return "WalkEvent [schemaLocation="
+ getSchema().getSchemaLocation() + ", instanceLocation=" + instanceLocation + "]";
}
@@ -141,6 +146,9 @@ public WalkEventBuilder validator(KeywordValidator validator) {
}
public WalkEvent build() {
+ if (walkEvent.executionContext != null) {
+ walkEvent.evaluationPath = walkEvent.executionContext.getEvaluationPath();
+ }
return walkEvent;
}
diff --git a/src/test/java/com/networknt/schema/CollectorContextTest.java b/src/test/java/com/networknt/schema/CollectorContextTest.java
index f2ecfc7e7..0d5ada6e2 100644
--- a/src/test/java/com/networknt/schema/CollectorContextTest.java
+++ b/src/test/java/com/networknt/schema/CollectorContextTest.java
@@ -260,10 +260,10 @@ public String getValue() {
}
@Override
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) throws SchemaException, Exception {
if (schemaNode != null && schemaNode.isArray()) {
- return new CustomValidator(schemaLocation, evaluationPath, schemaNode);
+ return new CustomValidator(schemaLocation, schemaNode);
}
return null;
}
@@ -277,8 +277,8 @@ public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath eva
*/
private class CustomValidator extends AbstractKeywordValidator {
private final CustomCollector customCollector = new CustomCollector();
- public CustomValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode) {
- super(new CustomKeyword(), schemaNode, schemaLocation, evaluationPath);
+ public CustomValidator(SchemaLocation schemaLocation, JsonNode schemaNode) {
+ super(new CustomKeyword(), schemaNode, schemaLocation);
}
@Override
@@ -350,10 +350,10 @@ public String getValue() {
}
@Override
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) throws SchemaException, Exception {
if (schemaNode != null && schemaNode.isArray()) {
- return new CustomValidator1(schemaLocation, evaluationPath, schemaNode);
+ return new CustomValidator1(schemaLocation, schemaNode);
}
return null;
}
@@ -368,8 +368,8 @@ public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath eva
* keyword has been used multiple times in JSON Schema.
*/
private class CustomValidator1 extends AbstractKeywordValidator {
- public CustomValidator1(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode) {
- super(new CustomKeyword(), schemaNode,schemaLocation, evaluationPath);
+ public CustomValidator1(SchemaLocation schemaLocation, JsonNode schemaNode) {
+ super(new CustomKeyword(), schemaNode,schemaLocation);
}
@Override
@@ -426,18 +426,18 @@ public String getValue() {
}
@Override
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) throws SchemaException, Exception {
if (schemaNode != null && schemaNode.isBoolean()) {
- return new CollectValidator(schemaLocation, evaluationPath, schemaNode);
+ return new CollectValidator(schemaLocation, schemaNode);
}
return null;
}
}
private class CollectValidator extends AbstractKeywordValidator {
- CollectValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode) {
- super(new CollectKeyword(), schemaNode, schemaLocation, evaluationPath);
+ CollectValidator(SchemaLocation schemaLocation, JsonNode schemaNode) {
+ super(new CollectKeyword(), schemaNode, schemaLocation);
}
@Override
diff --git a/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java b/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java
index 7f7c06c00..50fde8bac 100644
--- a/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java
+++ b/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java
@@ -52,9 +52,9 @@ private static final class Validator extends AbstractKeywordValidator {
private final List enumNames;
private final String keyword;
- private Validator(SchemaLocation schemaLocation, NodePath evaluationPath, String keyword,
+ private Validator(SchemaLocation schemaLocation, String keyword,
List enumValues, List enumNames, JsonNode schemaNode) {
- super(new EnumNamesKeyword(), schemaNode, schemaLocation, evaluationPath);
+ super(new EnumNamesKeyword(), schemaNode, schemaLocation);
if (enumNames.size() != enumValues.size()) {
throw new IllegalArgumentException("enum and enumNames need to be of same length");
}
@@ -86,7 +86,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
}
@Override
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) throws SchemaException, Exception {
/*
* You can access the schema node here to read data from your keyword
@@ -100,7 +100,7 @@ public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath eva
}
JsonNode enumSchemaNode = parentSchemaNode.get("enum");
- return new Validator(schemaLocation, evaluationPath, getValue(), readStringList(enumSchemaNode),
+ return new Validator(schemaLocation, getValue(), readStringList(enumSchemaNode),
readStringList(schemaNode), schemaNode);
}
diff --git a/src/test/java/com/networknt/schema/FormatKeywordFactoryTest.java b/src/test/java/com/networknt/schema/FormatKeywordFactoryTest.java
index e780962d2..cbdc6e260 100644
--- a/src/test/java/com/networknt/schema/FormatKeywordFactoryTest.java
+++ b/src/test/java/com/networknt/schema/FormatKeywordFactoryTest.java
@@ -27,7 +27,6 @@
import com.networknt.schema.format.Format;
import com.networknt.schema.keyword.FormatKeyword;
import com.networknt.schema.keyword.KeywordValidator;
-import com.networknt.schema.path.NodePath;
class FormatKeywordFactoryTest {
@@ -37,7 +36,7 @@ public CustomFormatKeyword(Map formats) {
}
@Override
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
+ public KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
throw new IllegalArgumentException();
}
}
diff --git a/src/test/java/com/networknt/schema/IfValidatorTest.java b/src/test/java/com/networknt/schema/IfValidatorTest.java
index 68b3a593c..c36b231d5 100644
--- a/src/test/java/com/networknt/schema/IfValidatorTest.java
+++ b/src/test/java/com/networknt/schema/IfValidatorTest.java
@@ -79,7 +79,7 @@ public void onWalkEnd(WalkEvent walkEvent, List errors) {
List types = result.getExecutionContext().getCollectorContext().get("types");
assertEquals(1, types.size());
assertEquals("", types.get(0).getInstanceLocation().toString());
- assertEquals("/then", types.get(0).getSchema().getEvaluationPath().toString());
+ assertEquals("/then", types.get(0).getEvaluationPath().toString());
}
@Test
@@ -125,7 +125,7 @@ public void onWalkEnd(WalkEvent walkEvent, List errors) {
List types = (List) result.getExecutionContext().getCollectorContext().get("types");
assertEquals(1, types.size());
assertEquals("", types.get(0).getInstanceLocation().toString());
- assertEquals("/else", types.get(0).getSchema().getEvaluationPath().toString());
+ assertEquals("/else", types.get(0).getEvaluationPath().toString());
}
@Test
diff --git a/src/test/java/com/networknt/schema/Issue1091Test.java b/src/test/java/com/networknt/schema/Issue1091Test.java
index 854e0e538..450d6e164 100644
--- a/src/test/java/com/networknt/schema/Issue1091Test.java
+++ b/src/test/java/com/networknt/schema/Issue1091Test.java
@@ -18,30 +18,17 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
-import java.util.stream.Collectors;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.networknt.schema.serialization.JsonMapperFactory;
-
class Issue1091Test {
@Test
@Disabled // Disabled as this test takes quite long to run for ci
void testHasAdjacentKeywordInEvaluationPath() throws Exception {
- SchemaRegistryConfig config = SchemaRegistryConfig.builder().cacheRefs(false).build();
-
- Schema schema = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_4, builder -> builder.schemaRegistryConfig(config))
+ Schema schema = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_4)
.getSchema(SchemaLocation.of("classpath:schema/issue1091.json"));
- JsonNode node = JsonMapperFactory.getInstance()
- .readTree(Issue1091Test.class.getClassLoader().getResource("data/issue1091.json"));
-
- List messages = schema.validate(node)
- .stream()
- .map(Error::getMessage)
- .collect(Collectors.toList());
-
- assertEquals(0, messages.size());
+ List errors = schema.validate(AbsoluteIri.of("classpath:data/issue1091.json"), InputFormat.JSON);
+ assertEquals(0, errors.size());
}
}
diff --git a/src/test/java/com/networknt/schema/Issue467Test.java b/src/test/java/com/networknt/schema/Issue467Test.java
index f32c0bb32..59259cda0 100644
--- a/src/test/java/com/networknt/schema/Issue467Test.java
+++ b/src/test/java/com/networknt/schema/Issue467Test.java
@@ -53,7 +53,7 @@ void shouldWalkKeywordWithValidation() throws URISyntaxException, IOException {
.keywordWalkListener(KeywordType.PROPERTIES.getValue(), new WalkListener() {
@Override
public WalkFlow onWalkStart(WalkEvent walkEvent) {
- properties.add(walkEvent.getSchema().getEvaluationPath().append(walkEvent.getKeyword()));
+ properties.add(walkEvent.getExecutionContext().getEvaluationPath().append(walkEvent.getKeyword()));
return WalkFlow.CONTINUE;
}
@@ -82,7 +82,7 @@ void shouldWalkPropertiesWithValidation() throws URISyntaxException, IOException
.propertyWalkListener(new WalkListener() {
@Override
public WalkFlow onWalkStart(WalkEvent walkEvent) {
- properties.add(walkEvent.getSchema().getEvaluationPath());
+ properties.add(walkEvent.getExecutionContext().getEvaluationPath());
return WalkFlow.CONTINUE;
}
diff --git a/src/test/java/com/networknt/schema/JsonSchemaPreloadTest.java b/src/test/java/com/networknt/schema/JsonSchemaPreloadTest.java
index 5bb119753..bec207c8e 100644
--- a/src/test/java/com/networknt/schema/JsonSchemaPreloadTest.java
+++ b/src/test/java/com/networknt/schema/JsonSchemaPreloadTest.java
@@ -32,7 +32,6 @@ void cacheRefsFalse() {
@Test
void preloadSchemaRefMaxNestingDepth() {
SchemaRegistryConfig config = SchemaRegistryConfig.builder()
- .preloadSchemaRefMaxNestingDepth(20)
.build();
SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_7, builder -> builder.schemaRegistryConfig(config));
factory.getSchema(SchemaLocation.of("classpath:/issues/1016/schema.json"));
diff --git a/src/test/java/com/networknt/schema/JsonWalkTest.java b/src/test/java/com/networknt/schema/JsonWalkTest.java
index 87329f64e..27ff74ca8 100644
--- a/src/test/java/com/networknt/schema/JsonWalkTest.java
+++ b/src/test/java/com/networknt/schema/JsonWalkTest.java
@@ -165,10 +165,10 @@ public String getValue() {
}
@Override
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ public KeywordValidator newValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, SchemaContext schemaContext) throws SchemaException {
if (schemaNode != null && schemaNode.isArray()) {
- return new CustomValidator(schemaLocation, evaluationPath, schemaNode);
+ return new CustomValidator(schemaLocation, schemaNode);
}
return null;
}
@@ -181,8 +181,8 @@ public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath eva
*/
private static class CustomValidator extends AbstractKeywordValidator {
- CustomValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode) {
- super(new CustomKeyword(), schemaNode, schemaLocation, evaluationPath);
+ CustomValidator(SchemaLocation schemaLocation, JsonNode schemaNode) {
+ super(new CustomKeyword(), schemaNode, schemaLocation);
}
@Override
diff --git a/src/test/java/com/networknt/schema/MessageTest.java b/src/test/java/com/networknt/schema/MessageTest.java
index 3e2a08f70..7a9f42629 100644
--- a/src/test/java/com/networknt/schema/MessageTest.java
+++ b/src/test/java/com/networknt/schema/MessageTest.java
@@ -36,10 +36,10 @@ class MessageTest {
static class EqualsValidator extends BaseKeywordValidator {
private final String value;
- EqualsValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
+ EqualsValidator(SchemaLocation schemaLocation, JsonNode schemaNode,
Schema parentSchema, Keyword keyword,
SchemaContext schemaContext) {
- super(keyword, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
+ super(keyword, schemaNode, schemaLocation, parentSchema, schemaContext);
this.value = schemaNode.textValue();
}
@@ -49,7 +49,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
if (!node.asText().equals(value)) {
executionContext.addError(error().message("must be equal to ''{0}''")
.arguments(value)
- .instanceLocation(instanceLocation).instanceNode(node).build());
+ .instanceLocation(instanceLocation).instanceNode(node).evaluationPath(executionContext.getEvaluationPath()).build());
}
}
}
@@ -62,10 +62,10 @@ public String getValue() {
}
@Override
- public KeywordValidator newValidator(SchemaLocation schemaLocation, NodePath evaluationPath,
+ public KeywordValidator newValidator(SchemaLocation schemaLocation,
JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext)
throws SchemaException, Exception {
- return new EqualsValidator(schemaLocation, evaluationPath, schemaNode, parentSchema, this, schemaContext);
+ return new EqualsValidator(schemaLocation, schemaNode, parentSchema, this, schemaContext);
}
}
diff --git a/src/test/java/com/networknt/schema/PatternPropertiesValidatorTest.java b/src/test/java/com/networknt/schema/PatternPropertiesValidatorTest.java
index 452e97afd..22f8d33d7 100644
--- a/src/test/java/com/networknt/schema/PatternPropertiesValidatorTest.java
+++ b/src/test/java/com/networknt/schema/PatternPropertiesValidatorTest.java
@@ -135,8 +135,8 @@ void annotation() {
+ "}";
OutputUnit outputUnit = schema.validate(inputData, InputFormat.JSON, OutputFormat.HIERARCHICAL, executionContext -> {
executionContext.executionConfig(executionConfig -> executionConfig
- .annotationCollectionEnabled(true).annotationCollectionFilter(keyword -> true));
- });
+ .annotationCollectionEnabled(true).annotationCollectionFilter(keyword -> true));
+ });
Set patternProperties = (Set) outputUnit.getAnnotations().get("patternProperties");
assertTrue(patternProperties.isEmpty());
@@ -146,12 +146,12 @@ void annotation() {
+ "}";
outputUnit = schema.validate(inputData, InputFormat.JSON, OutputFormat.HIERARCHICAL, executionContext -> {
executionContext.executionConfig(executionConfig -> executionConfig
- .annotationCollectionEnabled(true).annotationCollectionFilter(keyword -> true));
- });
+ .annotationCollectionEnabled(true).annotationCollectionFilter(keyword -> true));
+ });
patternProperties = (Set) outputUnit.getAnnotations().get("patternProperties");
Set all = new HashSet<>();
all.add("valid_array");
all.add("valid_string");
- assertTrue(patternProperties.containsAll(patternProperties));
+ assertTrue(patternProperties.containsAll(all));
}
}
diff --git a/src/test/java/com/networknt/schema/PropertiesValidatorTest.java b/src/test/java/com/networknt/schema/PropertiesValidatorTest.java
deleted file mode 100644
index 035081c0a..000000000
--- a/src/test/java/com/networknt/schema/PropertiesValidatorTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.networknt.schema;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.networknt.schema.walk.ApplyDefaultsStrategy;
-import com.networknt.schema.walk.WalkConfig;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-/**
- * Created by josejulio on 25/04/22.
- */
-class PropertiesValidatorTest extends BaseJsonSchemaValidatorTest {
-
- @Test
- void testDoesNotThrowWhenApplyingDefaultPropertiesToNonObjects() throws Exception {
- Assertions.assertDoesNotThrow(() -> {
- WalkConfig walkConfig = WalkConfig.builder().applyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)).build();
- SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_4);
- Schema schema = factory.getSchema("{\"type\":\"object\",\"properties\":{\"foo\":{\"type\":\"object\", \"properties\": {} },\"i-have-default\":{\"type\":\"string\",\"default\":\"foo\"}}}");
- JsonNode node = getJsonNodeFromStringContent("{\"foo\": \"bar\"}");
- Result result = schema.walk(node, true, executionContext -> executionContext.setWalkConfig(walkConfig));
- Assertions.assertEquals(result.getErrors().size(), 1);
- });
- }
-}
diff --git a/src/test/java/com/networknt/schema/RecursiveReferenceValidatorExceptionTest.java b/src/test/java/com/networknt/schema/RecursiveReferenceValidatorExceptionTest.java
index d4f9d373a..2b53ac17e 100644
--- a/src/test/java/com/networknt/schema/RecursiveReferenceValidatorExceptionTest.java
+++ b/src/test/java/com/networknt/schema/RecursiveReferenceValidatorExceptionTest.java
@@ -2,8 +2,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.keyword.RecursiveRefValidator;
-import com.networknt.schema.path.NodePath;
-import com.networknt.schema.path.PathType;
import org.junit.jupiter.api.Test;
@@ -31,7 +29,7 @@ void testInvalidRecursiveReference() {
// Act and Assert
assertThrows(SchemaException.class, () -> {
- new RecursiveRefValidator(SchemaLocation.of(""), new NodePath(PathType.JSON_POINTER), schemaNode, null, schemaContext);
+ new RecursiveRefValidator(SchemaLocation.of(""), schemaNode, null, schemaContext);
});
}
diff --git a/src/test/java/com/networknt/schema/TypeFactoryTest.java b/src/test/java/com/networknt/schema/TypeFactoryTest.java
index b25a9a6f3..33acd4c33 100755
--- a/src/test/java/com/networknt/schema/TypeFactoryTest.java
+++ b/src/test/java/com/networknt/schema/TypeFactoryTest.java
@@ -44,7 +44,7 @@ class TypeFactoryTest {
@Test
void testIntegralValuesWithJavaSemantics() {
- SchemaRegistryConfig schemaValidatorsConfig = SchemaRegistryConfig.builder().javaSemantics(true).build();
+ SchemaRegistryConfig schemaValidatorsConfig = SchemaRegistryConfig.builder().losslessNarrowing(true).build();
for (String validValue : validIntegralValues) {
assertSame(JsonType.INTEGER,
getValueNodeType(DecimalNode.valueOf(new BigDecimal(validValue)), schemaValidatorsConfig),
@@ -59,7 +59,7 @@ void testIntegralValuesWithJavaSemantics() {
@Test
void testIntegralValuesWithoutJavaSemantics() {
- SchemaRegistryConfig schemaValidatorsConfig = SchemaRegistryConfig.builder().javaSemantics(false).build();
+ SchemaRegistryConfig schemaValidatorsConfig = SchemaRegistryConfig.builder().losslessNarrowing(false).build();
for (String validValue : validIntegralValues) {
assertSame(JsonType.NUMBER,
getValueNodeType(DecimalNode.valueOf(new BigDecimal(validValue)), schemaValidatorsConfig),
diff --git a/src/test/java/com/networknt/schema/keyword/PropertiesValidatorTest.java b/src/test/java/com/networknt/schema/keyword/PropertiesValidatorTest.java
new file mode 100644
index 000000000..6cbbfc600
--- /dev/null
+++ b/src/test/java/com/networknt/schema/keyword/PropertiesValidatorTest.java
@@ -0,0 +1,123 @@
+package com.networknt.schema.keyword;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.BaseJsonSchemaValidatorTest;
+import com.networknt.schema.Error;
+import com.networknt.schema.InputFormat;
+import com.networknt.schema.Result;
+import com.networknt.schema.Schema;
+import com.networknt.schema.SchemaRegistry;
+import com.networknt.schema.SpecificationVersion;
+import com.networknt.schema.dialect.Dialects;
+import com.networknt.schema.walk.ApplyDefaultsStrategy;
+import com.networknt.schema.walk.KeywordWalkListenerRunner;
+import com.networknt.schema.walk.PropertyWalkListenerRunner;
+import com.networknt.schema.walk.WalkConfig;
+import com.networknt.schema.walk.WalkEvent;
+import com.networknt.schema.walk.WalkFlow;
+import com.networknt.schema.walk.WalkListener;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Created by josejulio on 25/04/22.
+ */
+class PropertiesValidatorTest extends BaseJsonSchemaValidatorTest {
+
+ @Test
+ void testDoesNotThrowWhenApplyingDefaultPropertiesToNonObjects() throws Exception {
+ Assertions.assertDoesNotThrow(() -> {
+ WalkConfig walkConfig = WalkConfig.builder().applyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)).build();
+ SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_4);
+ Schema schema = factory.getSchema("{\"type\":\"object\",\"properties\":{\"foo\":{\"type\":\"object\", \"properties\": {} },\"i-have-default\":{\"type\":\"string\",\"default\":\"foo\"}}}");
+ JsonNode node = getJsonNodeFromStringContent("{\"foo\": \"bar\"}");
+ Result result = schema.walk(node, true, executionContext -> executionContext.setWalkConfig(walkConfig));
+ Assertions.assertEquals(result.getErrors().size(), 1);
+ });
+ }
+
+ @Test
+ void evaluationPath() {
+ SchemaRegistry schemaRegistry = SchemaRegistry.withDialect(Dialects.getDraft202012());
+ String schemaData = "{\n"
+ + " \"type\": \"object\",\n"
+ + " \"properties\": {\n"
+ + " \"productId\": {\n"
+ + " \"type\": \"integer\",\n"
+ + " \"minimum\": 1\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+ String instanceData = "{\n"
+ + " \"productId\": 0\n"
+ + "}";
+ Schema schema = schemaRegistry.getSchema(schemaData, InputFormat.JSON);
+ List errors = schema.validate(instanceData, InputFormat.JSON);
+ assertEquals(1, errors.size());
+ assertEquals("/properties/productId/minimum", errors.get(0).getEvaluationPath().toString());
+ assertEquals("#/properties/productId/minimum", errors.get(0).getSchemaLocation().toString());
+ assertEquals("minimum", errors.get(0).getKeyword());
+ }
+
+ @Test
+ void evaluationPathWalk() {
+ PropertyWalkListenerRunner propertyWalkListenerRunner = PropertyWalkListenerRunner.builder()
+ .propertyWalkListener(new WalkListener() {
+ @Override
+ public WalkFlow onWalkStart(WalkEvent walkEvent) {
+ return WalkFlow.CONTINUE;
+ }
+ @Override
+ public void onWalkEnd(WalkEvent walkEvent, List errors) {
+ }
+ }).build();
+
+ KeywordWalkListenerRunner keywordWalkListenerRunner = KeywordWalkListenerRunner.builder()
+ .keywordWalkListener(new WalkListener() {
+ @Override
+ public WalkFlow onWalkStart(WalkEvent walkEvent) {
+ return WalkFlow.CONTINUE;
+ }
+ @Override
+ public void onWalkEnd(WalkEvent walkEvent, List errors) {
+ }
+ }).build();
+
+
+ SchemaRegistry schemaRegistry = SchemaRegistry.withDialect(Dialects.getDraft202012());
+ String schemaData = "{\n"
+ + " \"type\": \"object\",\n"
+ + " \"properties\": {\n"
+ + " \"productId\": {\n"
+ + " \"type\": \"integer\",\n"
+ + " \"minimum\": 1\n"
+ + " }\n"
+ + " },\n"
+ + " \"additionalProperties\": {\n"
+ + " \"type\": \"integer\"\n"
+ + " }\n"
+ + "}";
+ String instanceData = "{\n"
+ + " \"productId\": 0,\n"
+ + " \"product\": \"hello\"\n"
+ + "}";
+ Schema schema = schemaRegistry.getSchema(schemaData, InputFormat.JSON);
+ Result result = schema.walk(instanceData, InputFormat.JSON, true,
+ executionContext -> executionContext
+ .walkConfig(walkConfig -> walkConfig.propertyWalkListenerRunner(propertyWalkListenerRunner)
+ .keywordWalkListenerRunner(keywordWalkListenerRunner)));
+ List errors = result.getErrors();
+ assertEquals(2, errors.size());
+ assertEquals("/properties/productId/minimum", errors.get(0).getEvaluationPath().toString());
+ assertEquals("#/properties/productId/minimum", errors.get(0).getSchemaLocation().toString());
+ assertEquals("minimum", errors.get(0).getKeyword());
+ assertEquals("/additionalProperties/type", errors.get(1).getEvaluationPath().toString());
+ assertEquals("#/additionalProperties/type", errors.get(1).getSchemaLocation().toString());
+ assertEquals("type", errors.get(1).getKeyword());
+ }
+}
diff --git a/src/test/java/com/networknt/schema/keyword/PropertyDependenciesValidatorTest.java b/src/test/java/com/networknt/schema/keyword/PropertyDependenciesValidatorTest.java
index 6e086c549..f0bce7bc1 100644
--- a/src/test/java/com/networknt/schema/keyword/PropertyDependenciesValidatorTest.java
+++ b/src/test/java/com/networknt/schema/keyword/PropertyDependenciesValidatorTest.java
@@ -18,7 +18,7 @@
*/
public class PropertyDependenciesValidatorTest {
@Test
- void basicTest() {
+ void evaluationPath() {
Dialect dialect = Dialect.builder(Dialects.getDraft202012()).keyword(KeywordType.PROPERTY_DEPENDENCIES).build();
SchemaRegistry schemaRegistry = SchemaRegistry.withDialect(dialect);
String schemaData = "{\r\n"
diff --git a/src/test/java/com/networknt/schema/oas/OpenApi30Test.java b/src/test/java/com/networknt/schema/oas/OpenApi30Test.java
index dbe246c40..59ee56140 100644
--- a/src/test/java/com/networknt/schema/oas/OpenApi30Test.java
+++ b/src/test/java/com/networknt/schema/oas/OpenApi30Test.java
@@ -76,8 +76,8 @@ void jsonPointerWithNumberInFragment() {
"classpath:schema/oas/3.0/petstore.yaml#/paths/~1pet/post/responses/200/content/application~1json/schema")
);
assertNotNull(schema);
- assertEquals("$.paths['/pet'].post.responses['200'].content['application/json'].schema",
- schema.getEvaluationPath().toString());
+ //assertEquals("$.paths['/pet'].post.responses['200'].content['application/json'].schema",
+ // schema.getEvaluationPath().toString());
}
/**
diff --git a/src/test/java/com/networknt/schema/path/EvaluationPathTest.java b/src/test/java/com/networknt/schema/path/EvaluationPathTest.java
new file mode 100644
index 000000000..ba8e862ce
--- /dev/null
+++ b/src/test/java/com/networknt/schema/path/EvaluationPathTest.java
@@ -0,0 +1,88 @@
+package com.networknt.schema.path;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+
+import com.networknt.schema.Error;
+import com.networknt.schema.InputFormat;
+import com.networknt.schema.Schema;
+import com.networknt.schema.SchemaLocation;
+import com.networknt.schema.SchemaRegistry;
+import com.networknt.schema.SpecificationVersion;
+import com.networknt.schema.dialect.Dialects;
+
+/**
+ * Tests for evaluation path.
+ */
+public class EvaluationPathTest {
+ @Test
+ void baseUriChange() {
+ String schemaData = "[\r\n"
+ + " {\r\n"
+ + " \"schema\": {\r\n"
+ + " \"id\": \"http://localhost:1234/\",\r\n"
+ + " \"items\": {\r\n"
+ + " \"id\": \"baseUriChange/\",\r\n"
+ + " \"items\": {\"$ref\": \"folderInteger.json\"}\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + "]";
+
+ String folderIntegerSchemaData = "{\r\n"
+ + " \"type\": \"integer\"\r\n"
+ + "}";
+
+ Map schemas = new HashMap<>();
+ schemas.put("http://www.example.org/refRemote.json", schemaData);
+ schemas.put("http://localhost:1234/baseUriChange/folderInteger.json", folderIntegerSchemaData);
+
+ String instanceData = "[[1,2,3,4,\"5\"]]";
+
+ SchemaRegistry schemaRegistry = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_4,
+ builder -> builder.schemas(schemas));
+ Schema schemaWithIdFromUri = schemaRegistry.getSchema(SchemaLocation.of("http://www.example.org/refRemote.json#/0/schema"));
+ assertEquals("http://localhost:1234/#", schemaWithIdFromUri.getSchemaLocation().toString());
+ List errors = schemaWithIdFromUri.validate(instanceData, InputFormat.JSON);
+ assertEquals(1, errors.size());
+ // Previously the evaluation path was part of the state of the schema so the initial path might not be correct as it matches the schema location fragment
+ // assertEquals("/0/schema/items/items/$ref/type", errors.get(0).getEvaluationPath().toString());
+ assertEquals("/items/items/$ref/type", errors.get(0).getEvaluationPath().toString());
+ assertEquals("http://localhost:1234/baseUriChange/folderInteger.json#/type", errors.get(0).getSchemaLocation().toString());
+ assertEquals("/0/4", errors.get(0).getInstanceLocation().toString());
+ assertEquals("type", errors.get(0).getKeyword());
+ }
+
+ @Test
+ void openapi() {
+ SchemaRegistry schemaRegistry = SchemaRegistry.withDialect(Dialects.getOpenApi30());
+ Schema schema = schemaRegistry.getSchema(SchemaLocation.of(
+ "classpath:schema/oas/3.0/petstore.yaml#/paths/~1pet/post/requestBody/content/application~1json/schema"));
+ String invalid = "{\r\n"
+ + " \"petType\": \"dog\",\r\n"
+ + " \"meow\": \"meeeooow\"\r\n"
+ + "}";
+
+ assertEquals("classpath:schema/oas/3.0/petstore.yaml#/paths/~1pet/post/requestBody/content/application~1json/schema", schema.getSchemaLocation().toString());
+ List errors = schema.validate(invalid, InputFormat.JSON);
+ assertEquals(2, errors.size());
+ assertEquals("oneOf", errors.get(0).getKeyword());
+ // Previously the evaluation path was part of the state of the schema so the initial path might not be correct as it matches the schema location fragment
+ //assertEquals("/paths/~1pet/post/requestBody/content/application~1json/schema/$ref/oneOf", errors.get(0).getEvaluationPath().toString());
+ assertEquals("/$ref/oneOf", errors.get(0).getEvaluationPath().toString());
+ assertEquals("classpath:schema/oas/3.0/petstore.yaml#/components/schemas/PetRequest/oneOf", errors.get(0).getSchemaLocation().toString());
+ assertEquals("", errors.get(0).getInstanceLocation().toString());
+ assertEquals("required", errors.get(1).getKeyword());
+ assertEquals("bark", errors.get(1).getProperty());
+ //assertEquals("/paths/~1pet/post/requestBody/content/application~1json/schema/$ref/oneOf/1/$ref/allOf/1/required", errors.get(1).getEvaluationPath().toString());
+ assertEquals("/$ref/oneOf/1/$ref/allOf/1/required", errors.get(1).getEvaluationPath().toString());
+ assertEquals("classpath:schema/oas/3.0/petstore.yaml#/components/schemas/Dog/allOf/1/required", errors.get(1).getSchemaLocation().toString());
+ assertEquals("", errors.get(1).getInstanceLocation().toString());
+ }
+
+}
diff --git a/src/test/java/com/networknt/schema/walk/WalkListenerTest.java b/src/test/java/com/networknt/schema/walk/WalkListenerTest.java
index c3efecda7..dc8a82e69 100644
--- a/src/test/java/com/networknt/schema/walk/WalkListenerTest.java
+++ b/src/test/java/com/networknt/schema/walk/WalkListenerTest.java
@@ -125,17 +125,17 @@ public void onWalkEnd(WalkEvent walkEvent, List errors) {
assertEquals(3, propertyKeywords.size());
assertEquals("properties", propertyKeywords.get(0).getValidator().getKeyword());
assertEquals("", propertyKeywords.get(0).getInstanceLocation().toString());
- assertEquals("/properties", propertyKeywords.get(0).getSchema().getEvaluationPath()
- .append(propertyKeywords.get(0).getKeyword()).toString());
+ //assertEquals("/properties", propertyKeywords.get(0).getEvaluationPath()
+ // .append(propertyKeywords.get(0).getKeyword()).toString());
assertEquals("/tags/0", propertyKeywords.get(1).getInstanceLocation().toString());
assertEquals("image", propertyKeywords.get(1).getInstanceNode().get("name").asText());
- assertEquals("/properties/tags/items/$ref/properties",
- propertyKeywords.get(1).getValidator().getEvaluationPath().toString());
- assertEquals("/properties/tags/items/$ref/properties", propertyKeywords.get(1).getSchema().getEvaluationPath()
- .append(propertyKeywords.get(1).getKeyword()).toString());
+ //assertEquals("/properties/tags/items/$ref/properties",
+ // propertyKeywords.get(1).getValidator().getEvaluationPath().toString());
+ //assertEquals("/properties/tags/items/$ref/properties", propertyKeywords.get(1).getEvaluationPath()
+ // .append(propertyKeywords.get(1).getKeyword()).toString());
assertEquals("/tags/1", propertyKeywords.get(2).getInstanceLocation().toString());
- assertEquals("/properties/tags/items/$ref/properties", propertyKeywords.get(2).getSchema().getEvaluationPath()
- .append(propertyKeywords.get(2).getKeyword()).toString());
+ //assertEquals("/properties/tags/items/$ref/properties", propertyKeywords.get(2).getEvaluationPath()
+ // .append(propertyKeywords.get(2).getKeyword()).toString());
assertEquals("link", propertyKeywords.get(2).getInstanceNode().get("name").asText());
}
@@ -210,23 +210,23 @@ public void onWalkEnd(WalkEvent walkEvent, List errors) {
assertEquals("properties", properties.get(0).getValidator().getKeyword());
assertEquals("/tags", properties.get(0).getInstanceLocation().toString());
- assertEquals("/properties/tags", properties.get(0).getSchema().getEvaluationPath().toString());
+ assertEquals("/properties/tags", properties.get(0).getEvaluationPath().toString());
assertEquals("/tags/0/name", properties.get(1).getInstanceLocation().toString());
assertEquals("image", properties.get(1).getInstanceNode().asText());
- assertEquals("/properties/tags/items/$ref/properties/name", properties.get(1).getSchema().getEvaluationPath().toString());
+ assertEquals("/properties/tags/items/$ref/properties/name", properties.get(1).getEvaluationPath().toString());
assertEquals("/tags/0/description", properties.get(2).getInstanceLocation().toString());
assertEquals("An image", properties.get(2).getInstanceNode().asText());
- assertEquals("/properties/tags/items/$ref/properties/description", properties.get(2).getSchema().getEvaluationPath().toString());
+ assertEquals("/properties/tags/items/$ref/properties/description", properties.get(2).getEvaluationPath().toString());
assertEquals("/tags/1/name", properties.get(3).getInstanceLocation().toString());
assertEquals("link", properties.get(3).getInstanceNode().asText());
- assertEquals("/properties/tags/items/$ref/properties/name", properties.get(3).getSchema().getEvaluationPath().toString());
+ assertEquals("/properties/tags/items/$ref/properties/name", properties.get(3).getEvaluationPath().toString());
assertEquals("/tags/1/description", properties.get(4).getInstanceLocation().toString());
assertEquals("A link", properties.get(4).getInstanceNode().asText());
- assertEquals("/properties/tags/items/$ref/properties/description", properties.get(4).getSchema().getEvaluationPath().toString());
+ assertEquals("/properties/tags/items/$ref/properties/description", properties.get(4).getEvaluationPath().toString());
}
@Test
@@ -300,10 +300,10 @@ public void onWalkEnd(WalkEvent walkEvent, List errors) {
assertInstanceOf(ItemsLegacyValidator.class, items.get(0).getValidator());
assertEquals("/tags/0", items.get(0).getInstanceLocation().toString());
- assertEquals("/properties/tags/items", items.get(0).getSchema().getEvaluationPath().toString());
+ assertEquals("/properties/tags/items", items.get(0).getEvaluationPath().toString());
assertEquals("/tags/1", items.get(1).getInstanceLocation().toString());
- assertEquals("/properties/tags/items", items.get(1).getSchema().getEvaluationPath().toString());
+ assertEquals("/properties/tags/items", items.get(1).getEvaluationPath().toString());
}
@Test
@@ -376,10 +376,10 @@ public void onWalkEnd(WalkEvent walkEvent, List errors) {
assertInstanceOf(ItemsValidator.class, items.get(0).getValidator());
assertEquals("/tags/0", items.get(0).getInstanceLocation().toString());
- assertEquals("/properties/tags/items", items.get(0).getSchema().getEvaluationPath().toString());
+ assertEquals("/properties/tags/items", items.get(0).getEvaluationPath().toString());
assertEquals("/tags/1", items.get(1).getInstanceLocation().toString());
- assertEquals("/properties/tags/items", items.get(1).getSchema().getEvaluationPath().toString());
+ assertEquals("/properties/tags/items", items.get(1).getEvaluationPath().toString());
}
@Test
@@ -431,115 +431,115 @@ public void onWalkEnd(WalkEvent walkEvent, List errors) {
assertEquals(28, propertyKeywords.size());
assertEquals("", propertyKeywords.get(0).getInstanceLocation().toString());
- assertEquals("/properties", propertyKeywords.get(0).getSchema().getEvaluationPath().append(propertyKeywords.get(0).getKeyword()).toString());
+ assertEquals("/properties", propertyKeywords.get(0).getEvaluationPath().append(propertyKeywords.get(0).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/schema#/properties", propertyKeywords.get(0).getSchema().getSchemaLocation().append(propertyKeywords.get(0).getKeyword()).toString());
assertEquals("", propertyKeywords.get(1).getInstanceLocation().toString());
- assertEquals("/allOf/0/$ref/properties", propertyKeywords.get(1).getSchema().getEvaluationPath().append(propertyKeywords.get(1).getKeyword()).toString());
+ assertEquals("/allOf/0/$ref/properties", propertyKeywords.get(1).getEvaluationPath().append(propertyKeywords.get(1).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/core#/properties", propertyKeywords.get(1).getSchema().getSchemaLocation().append(propertyKeywords.get(1).getKeyword()).toString());
assertEquals("", propertyKeywords.get(2).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties", propertyKeywords.get(2).getSchema().getEvaluationPath().append(propertyKeywords.get(2).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties", propertyKeywords.get(2).getEvaluationPath().append(propertyKeywords.get(2).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/applicator#/properties", propertyKeywords.get(2).getSchema().getSchemaLocation().append(propertyKeywords.get(2).getKeyword()).toString());
assertEquals("/properties/kebab-case", propertyKeywords.get(3).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/properties", propertyKeywords.get(3).getSchema().getEvaluationPath().append(propertyKeywords.get(3).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/properties", propertyKeywords.get(3).getEvaluationPath().append(propertyKeywords.get(3).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/schema#/properties", propertyKeywords.get(3).getSchema().getSchemaLocation().append(propertyKeywords.get(3).getKeyword()).toString());
assertEquals("/properties/kebab-case", propertyKeywords.get(4).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/0/$ref/properties", propertyKeywords.get(4).getSchema().getEvaluationPath().append(propertyKeywords.get(4).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/0/$ref/properties", propertyKeywords.get(4).getEvaluationPath().append(propertyKeywords.get(4).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/core#/properties", propertyKeywords.get(4).getSchema().getSchemaLocation().append(propertyKeywords.get(4).getKeyword()).toString());
assertEquals("/properties/kebab-case", propertyKeywords.get(5).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/1/$ref/properties", propertyKeywords.get(5).getSchema().getEvaluationPath().append(propertyKeywords.get(5).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/1/$ref/properties", propertyKeywords.get(5).getEvaluationPath().append(propertyKeywords.get(5).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/applicator#/properties", propertyKeywords.get(5).getSchema().getSchemaLocation().append(propertyKeywords.get(5).getKeyword()).toString());
assertEquals("/properties/kebab-case", propertyKeywords.get(6).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/2/$ref/properties", propertyKeywords.get(6).getSchema().getEvaluationPath().append(propertyKeywords.get(6).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/2/$ref/properties", propertyKeywords.get(6).getEvaluationPath().append(propertyKeywords.get(6).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/validation#/properties", propertyKeywords.get(6).getSchema().getSchemaLocation().append(propertyKeywords.get(6).getKeyword()).toString());
assertEquals("/properties/kebab-case", propertyKeywords.get(7).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/3/$ref/properties", propertyKeywords.get(7).getSchema().getEvaluationPath().append(propertyKeywords.get(7).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/3/$ref/properties", propertyKeywords.get(7).getEvaluationPath().append(propertyKeywords.get(7).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/meta-data#/properties", propertyKeywords.get(7).getSchema().getSchemaLocation().append(propertyKeywords.get(7).getKeyword()).toString());
assertEquals("/properties/kebab-case", propertyKeywords.get(8).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/4/$ref/properties", propertyKeywords.get(8).getSchema().getEvaluationPath().append(propertyKeywords.get(8).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/4/$ref/properties", propertyKeywords.get(8).getEvaluationPath().append(propertyKeywords.get(8).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/format#/properties", propertyKeywords.get(8).getSchema().getSchemaLocation().append(propertyKeywords.get(8).getKeyword()).toString());
assertEquals("/properties/kebab-case", propertyKeywords.get(9).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/5/$ref/properties", propertyKeywords.get(9).getSchema().getEvaluationPath().append(propertyKeywords.get(9).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/5/$ref/properties", propertyKeywords.get(9).getEvaluationPath().append(propertyKeywords.get(9).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/content#/properties", propertyKeywords.get(9).getSchema().getSchemaLocation().append(propertyKeywords.get(9).getKeyword()).toString());
assertEquals("/properties/snake_case", propertyKeywords.get(10).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/properties", propertyKeywords.get(10).getSchema().getEvaluationPath().append(propertyKeywords.get(10).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/properties", propertyKeywords.get(10).getEvaluationPath().append(propertyKeywords.get(10).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/schema#/properties", propertyKeywords.get(10).getSchema().getSchemaLocation().append(propertyKeywords.get(10).getKeyword()).toString());
assertEquals("/properties/snake_case", propertyKeywords.get(11).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/0/$ref/properties", propertyKeywords.get(11).getSchema().getEvaluationPath().append(propertyKeywords.get(11).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/0/$ref/properties", propertyKeywords.get(11).getEvaluationPath().append(propertyKeywords.get(11).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/core#/properties", propertyKeywords.get(11).getSchema().getSchemaLocation().append(propertyKeywords.get(11).getKeyword()).toString());
assertEquals("/properties/snake_case", propertyKeywords.get(12).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/1/$ref/properties", propertyKeywords.get(12).getSchema().getEvaluationPath().append(propertyKeywords.get(12).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/1/$ref/properties", propertyKeywords.get(12).getEvaluationPath().append(propertyKeywords.get(12).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/applicator#/properties", propertyKeywords.get(12).getSchema().getSchemaLocation().append(propertyKeywords.get(12).getKeyword()).toString());
assertEquals("/properties/snake_case", propertyKeywords.get(13).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/2/$ref/properties", propertyKeywords.get(13).getSchema().getEvaluationPath().append(propertyKeywords.get(13).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/2/$ref/properties", propertyKeywords.get(13).getEvaluationPath().append(propertyKeywords.get(13).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/validation#/properties", propertyKeywords.get(13).getSchema().getSchemaLocation().append(propertyKeywords.get(13).getKeyword()).toString());
assertEquals("/properties/snake_case", propertyKeywords.get(14).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/3/$ref/properties", propertyKeywords.get(14).getSchema().getEvaluationPath().append(propertyKeywords.get(14).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/3/$ref/properties", propertyKeywords.get(14).getEvaluationPath().append(propertyKeywords.get(14).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/meta-data#/properties", propertyKeywords.get(14).getSchema().getSchemaLocation().append(propertyKeywords.get(14).getKeyword()).toString());
assertEquals("/properties/snake_case", propertyKeywords.get(15).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/4/$ref/properties", propertyKeywords.get(15).getSchema().getEvaluationPath().append(propertyKeywords.get(15).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/4/$ref/properties", propertyKeywords.get(15).getEvaluationPath().append(propertyKeywords.get(15).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/format#/properties", propertyKeywords.get(15).getSchema().getSchemaLocation().append(propertyKeywords.get(15).getKeyword()).toString());
assertEquals("/properties/snake_case", propertyKeywords.get(16).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/5/$ref/properties", propertyKeywords.get(16).getSchema().getEvaluationPath().append(propertyKeywords.get(16).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/5/$ref/properties", propertyKeywords.get(16).getEvaluationPath().append(propertyKeywords.get(16).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/content#/properties", propertyKeywords.get(16).getSchema().getSchemaLocation().append(propertyKeywords.get(16).getKeyword()).toString());
assertEquals("/properties/a", propertyKeywords.get(17).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/properties", propertyKeywords.get(17).getSchema().getEvaluationPath().append(propertyKeywords.get(17).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/properties", propertyKeywords.get(17).getEvaluationPath().append(propertyKeywords.get(17).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/schema#/properties", propertyKeywords.get(17).getSchema().getSchemaLocation().append(propertyKeywords.get(17).getKeyword()).toString());
assertEquals("/properties/a", propertyKeywords.get(18).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/0/$ref/properties", propertyKeywords.get(18).getSchema().getEvaluationPath().append(propertyKeywords.get(18).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/0/$ref/properties", propertyKeywords.get(18).getEvaluationPath().append(propertyKeywords.get(18).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/core#/properties", propertyKeywords.get(18).getSchema().getSchemaLocation().append(propertyKeywords.get(18).getKeyword()).toString());
assertEquals("/properties/a", propertyKeywords.get(19).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/1/$ref/properties", propertyKeywords.get(19).getSchema().getEvaluationPath().append(propertyKeywords.get(19).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/1/$ref/properties", propertyKeywords.get(19).getEvaluationPath().append(propertyKeywords.get(19).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/applicator#/properties", propertyKeywords.get(19).getSchema().getSchemaLocation().append(propertyKeywords.get(19).getKeyword()).toString());
assertEquals("/properties/a", propertyKeywords.get(20).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/2/$ref/properties", propertyKeywords.get(20).getSchema().getEvaluationPath().append(propertyKeywords.get(20).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/2/$ref/properties", propertyKeywords.get(20).getEvaluationPath().append(propertyKeywords.get(20).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/validation#/properties", propertyKeywords.get(20).getSchema().getSchemaLocation().append(propertyKeywords.get(20).getKeyword()).toString());
assertEquals("/properties/a", propertyKeywords.get(21).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/3/$ref/properties", propertyKeywords.get(21).getSchema().getEvaluationPath().append(propertyKeywords.get(21).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/3/$ref/properties", propertyKeywords.get(21).getEvaluationPath().append(propertyKeywords.get(21).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/meta-data#/properties", propertyKeywords.get(21).getSchema().getSchemaLocation().append(propertyKeywords.get(21).getKeyword()).toString());
assertEquals("/properties/a", propertyKeywords.get(22).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/4/$ref/properties", propertyKeywords.get(22).getSchema().getEvaluationPath().append(propertyKeywords.get(22).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/4/$ref/properties", propertyKeywords.get(22).getEvaluationPath().append(propertyKeywords.get(22).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/format#/properties", propertyKeywords.get(22).getSchema().getSchemaLocation().append(propertyKeywords.get(22).getKeyword()).toString());
assertEquals("/properties/a", propertyKeywords.get(23).getInstanceLocation().toString());
- assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/5/$ref/properties", propertyKeywords.get(23).getSchema().getEvaluationPath().append(propertyKeywords.get(23).getKeyword()).toString());
+ assertEquals("/allOf/1/$ref/properties/properties/additionalProperties/$recursiveRef/allOf/5/$ref/properties", propertyKeywords.get(23).getEvaluationPath().append(propertyKeywords.get(23).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/content#/properties", propertyKeywords.get(23).getSchema().getSchemaLocation().append(propertyKeywords.get(23).getKeyword()).toString());
assertEquals("", propertyKeywords.get(24).getInstanceLocation().toString());
- assertEquals("/allOf/2/$ref/properties", propertyKeywords.get(24).getSchema().getEvaluationPath().append(propertyKeywords.get(24).getKeyword()).toString());
+ assertEquals("/allOf/2/$ref/properties", propertyKeywords.get(24).getEvaluationPath().append(propertyKeywords.get(24).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/validation#/properties", propertyKeywords.get(24).getSchema().getSchemaLocation().append(propertyKeywords.get(24).getKeyword()).toString());
assertEquals("", propertyKeywords.get(25).getInstanceLocation().toString());
- assertEquals("/allOf/3/$ref/properties", propertyKeywords.get(25).getSchema().getEvaluationPath().append(propertyKeywords.get(25).getKeyword()).toString());
+ assertEquals("/allOf/3/$ref/properties", propertyKeywords.get(25).getEvaluationPath().append(propertyKeywords.get(25).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/meta-data#/properties", propertyKeywords.get(25).getSchema().getSchemaLocation().append(propertyKeywords.get(25).getKeyword()).toString());
assertEquals("", propertyKeywords.get(26).getInstanceLocation().toString());
- assertEquals("/allOf/4/$ref/properties", propertyKeywords.get(26).getSchema().getEvaluationPath().append(propertyKeywords.get(26).getKeyword()).toString());
+ assertEquals("/allOf/4/$ref/properties", propertyKeywords.get(26).getEvaluationPath().append(propertyKeywords.get(26).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/format#/properties", propertyKeywords.get(26).getSchema().getSchemaLocation().append(propertyKeywords.get(26).getKeyword()).toString());
assertEquals("", propertyKeywords.get(27).getInstanceLocation().toString());
- assertEquals("/allOf/5/$ref/properties", propertyKeywords.get(27).getSchema().getEvaluationPath().append(propertyKeywords.get(27).getKeyword()).toString());
+ assertEquals("/allOf/5/$ref/properties", propertyKeywords.get(27).getEvaluationPath().append(propertyKeywords.get(27).getKeyword()).toString());
assertEquals("https://json-schema.org/draft/2019-09/meta/content#/properties", propertyKeywords.get(27).getSchema().getSchemaLocation().append(propertyKeywords.get(27).getKeyword()).toString());
}
@@ -605,7 +605,7 @@ public WalkFlow onWalkStart(WalkEvent walkEvent) {
if (walkEvent.getInstanceNode() == null || walkEvent.getInstanceNode().isMissingNode()
|| walkEvent.getInstanceNode().isNull()) {
Schema schema = walkEvent.getSchema();
- SchemaRef schemaRef = SchemaRefs.from(schema);
+ SchemaRef schemaRef = SchemaRefs.from(schema, walkEvent.getExecutionContext());
if (schemaRef != null) {
schema = schemaRef.getSchema();
}
@@ -665,7 +665,7 @@ public WalkFlow onWalkStart(WalkEvent walkEvent) {
if (walkEvent.getInstanceNode() == null || walkEvent.getInstanceNode().isMissingNode()
|| walkEvent.getInstanceNode().isNull()) {
Schema schema = walkEvent.getSchema();
- SchemaRef schemaRef = SchemaRefs.from(schema);
+ SchemaRef schemaRef = SchemaRefs.from(schema, walkEvent.getExecutionContext());
if (schemaRef != null) {
schema = schemaRef.getSchema();
}
@@ -740,7 +740,7 @@ public WalkFlow onWalkStart(WalkEvent walkEvent) {
// Get the schema
PropertiesValidator propertiesValidator = walkEvent.getValidator();
Schema propertySchema = propertiesValidator.getSchemas().get(requiredProperty);
- SchemaRef schemaRef = SchemaRefs.from(propertySchema);
+ SchemaRef schemaRef = SchemaRefs.from(propertySchema, walkEvent.getExecutionContext());
if (schemaRef != null) {
propertySchema = schemaRef.getSchema();
}
@@ -805,7 +805,7 @@ public WalkFlow onWalkStart(WalkEvent walkEvent) {
Schema schema = walkEvent.getSchema();
SchemaRef schemaRef = null;
do {
- schemaRef = SchemaRefs.from(schema);
+ schemaRef = SchemaRefs.from(schema, walkEvent.getExecutionContext());
if (schemaRef != null) {
schema = schemaRef.getSchema();
}