Skip to content

Commit b2d6135

Browse files
fduttonFaron Dutton
andauthored
Simplifies how evaluated properties and array items are tracked (#790)
* Simplifies how evaluated properties and array items are tracked in order to improve performance. Resolves #721 * Corrects issue with deserializing JSON Schema Test Suite tests. Resolves #804 * Adds configuration parameters to disable unevaluatedItems and unevaluatedProperties analysis --------- Co-authored-by: Faron Dutton <[email protected]>
1 parent 3c9d1d4 commit b2d6135

20 files changed

+602
-415
lines changed

src/main/java/com/networknt/schema/AllOfValidator.java

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import com.fasterxml.jackson.databind.JsonNode;
2222
import com.fasterxml.jackson.databind.node.ObjectNode;
23+
import com.networknt.schema.CollectorContext.Scope;
24+
2325
import org.slf4j.Logger;
2426
import org.slf4j.LoggerFactory;
2527

@@ -47,21 +49,11 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
4749

4850
Set<ValidationMessage> childSchemaErrors = new LinkedHashSet<>();
4951

50-
Collection<String> newEvaluatedItems = Collections.emptyList();
51-
Collection<String> newEvaluatedProperties = Collections.emptyList();
52-
5352
for (JsonSchema schema : this.schemas) {
54-
// As AllOf might contain multiple schemas take a backup of evaluated stuff.
55-
Collection<String> backupEvaluatedItems = collectorContext.getEvaluatedItems();
56-
Collection<String> backupEvaluatedProperties = collectorContext.getEvaluatedProperties();
57-
5853
Set<ValidationMessage> localErrors = new HashSet<>();
5954

55+
Scope parentScope = collectorContext.enterDynamicScope();
6056
try {
61-
// Make the evaluated lists empty.
62-
collectorContext.resetEvaluatedItems();
63-
collectorContext.resetEvaluatedProperties();
64-
6557
if (!state.isWalkEnabled()) {
6658
localErrors = schema.validate(node, rootNode, at);
6759
} else {
@@ -70,12 +62,6 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
7062

7163
childSchemaErrors.addAll(localErrors);
7264

73-
// Keep Collecting total evaluated properties.
74-
if (localErrors.isEmpty()) {
75-
newEvaluatedItems = collectorContext.getEvaluatedItems();
76-
newEvaluatedProperties = collectorContext.getEvaluatedProperties();
77-
}
78-
7965
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
8066
final Iterator<JsonNode> arrayElements = this.schemaNode.elements();
8167
while (arrayElements.hasNext()) {
@@ -108,14 +94,10 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
10894
}
10995
}
11096
} finally {
111-
collectorContext.setEvaluatedItems(backupEvaluatedItems);
112-
collectorContext.setEvaluatedProperties(backupEvaluatedProperties);
97+
Scope scope = collectorContext.exitDynamicScope();
11398
if (localErrors.isEmpty()) {
114-
collectorContext.getEvaluatedItems().addAll(newEvaluatedItems);
115-
collectorContext.getEvaluatedProperties().addAll(newEvaluatedProperties);
99+
parentScope.mergeWith(scope);
116100
}
117-
newEvaluatedItems = Collections.emptyList();
118-
newEvaluatedProperties = Collections.emptyList();
119101
}
120102
}
121103

src/main/java/com/networknt/schema/AnyOfValidator.java

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package com.networknt.schema;
1818

1919
import com.fasterxml.jackson.databind.JsonNode;
20+
import com.networknt.schema.CollectorContext.Scope;
21+
2022
import org.slf4j.Logger;
2123
import org.slf4j.LoggerFactory;
2224

@@ -61,64 +63,64 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
6163

6264
Set<ValidationMessage> allErrors = new LinkedHashSet<>();
6365

64-
// As anyOf might contain multiple schemas take a backup of evaluated stuff.
65-
Collection<String> backupEvaluatedItems = collectorContext.getEvaluatedItems();
66-
Collection<String> backupEvaluatedProperties = collectorContext.getEvaluatedProperties();
67-
68-
// Make the evaluated lists empty.
69-
collectorContext.resetEvaluatedItems();
70-
collectorContext.resetEvaluatedProperties();
71-
66+
Scope grandParentScope = collectorContext.enterDynamicScope();
7267
try {
7368
int numberOfValidSubSchemas = 0;
74-
for (int i = 0; i < this.schemas.size(); ++i) {
75-
JsonSchema schema = this.schemas.get(i);
76-
state.setMatchedNode(initialHasMatchedNode);
77-
Set<ValidationMessage> errors;
78-
79-
if (schema.hasTypeValidator()) {
80-
TypeValidator typeValidator = schema.getTypeValidator();
81-
//If schema has type validator and node type doesn't match with schemaType then ignore it
82-
//For union type, it is a must to call TypeValidator
83-
if (typeValidator.getSchemaType() != JsonType.UNION && !typeValidator.equalsToSchemaType(node)) {
84-
allErrors.add(buildValidationMessage(at, typeValidator.getSchemaType().toString()));
85-
continue;
69+
for (JsonSchema schema: this.schemas) {
70+
Set<ValidationMessage> errors = Collections.emptySet();
71+
Scope parentScope = collectorContext.enterDynamicScope();
72+
try {
73+
state.setMatchedNode(initialHasMatchedNode);
74+
75+
if (schema.hasTypeValidator()) {
76+
TypeValidator typeValidator = schema.getTypeValidator();
77+
//If schema has type validator and node type doesn't match with schemaType then ignore it
78+
//For union type, it is a must to call TypeValidator
79+
if (typeValidator.getSchemaType() != JsonType.UNION && !typeValidator.equalsToSchemaType(node)) {
80+
allErrors.add(buildValidationMessage(at, typeValidator.getSchemaType().toString()));
81+
continue;
82+
}
8683
}
87-
}
88-
if (!state.isWalkEnabled()) {
89-
errors = schema.validate(node, rootNode, at);
90-
} else {
91-
errors = schema.walk(node, rootNode, at, true);
92-
}
93-
94-
// check if any validation errors have occurred
95-
if (errors.isEmpty()) {
96-
// check whether there are no errors HOWEVER we have validated the exact validator
97-
if (!state.hasMatchedNode()) {
98-
continue;
84+
if (!state.isWalkEnabled()) {
85+
errors = schema.validate(node, rootNode, at);
86+
} else {
87+
errors = schema.walk(node, rootNode, at, true);
9988
}
100-
// we found a valid subschema, so increase counter
101-
numberOfValidSubSchemas++;
102-
}
10389

104-
if (errors.isEmpty() && (!this.validationContext.getConfig().isOpenAPI3StyleDiscriminators())) {
105-
// Clear all errors.
106-
allErrors.clear();
107-
// return empty errors.
108-
return errors;
109-
} else if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
110-
if (this.discriminatorContext.isDiscriminatorMatchFound()) {
111-
if (!errors.isEmpty()) {
112-
errors.add(buildValidationMessage(at, DISCRIMINATOR_REMARK));
113-
allErrors.addAll(errors);
114-
} else {
115-
// Clear all errors.
116-
allErrors.clear();
90+
// check if any validation errors have occurred
91+
if (errors.isEmpty()) {
92+
// check whether there are no errors HOWEVER we have validated the exact validator
93+
if (!state.hasMatchedNode()) {
94+
continue;
11795
}
96+
// we found a valid subschema, so increase counter
97+
numberOfValidSubSchemas++;
98+
}
99+
100+
if (errors.isEmpty() && (!this.validationContext.getConfig().isOpenAPI3StyleDiscriminators())) {
101+
// Clear all errors.
102+
allErrors.clear();
103+
// return empty errors.
118104
return errors;
105+
} else if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
106+
if (this.discriminatorContext.isDiscriminatorMatchFound()) {
107+
if (!errors.isEmpty()) {
108+
allErrors.addAll(errors);
109+
allErrors.add(buildValidationMessage(at, DISCRIMINATOR_REMARK));
110+
} else {
111+
// Clear all errors.
112+
allErrors.clear();
113+
}
114+
return errors;
115+
}
116+
}
117+
allErrors.addAll(errors);
118+
} finally {
119+
Scope scope = collectorContext.exitDynamicScope();
120+
if (errors.isEmpty()) {
121+
parentScope.mergeWith(scope);
119122
}
120123
}
121-
allErrors.addAll(errors);
122124
}
123125

124126
// determine only those errors which are NOT of type "required" property missing
@@ -138,14 +140,12 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
138140
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
139141
this.validationContext.leaveDiscriminatorContextImmediately(at);
140142
}
143+
144+
Scope parentScope = collectorContext.exitDynamicScope();
141145
if (allErrors.isEmpty()) {
142146
state.setMatchedNode(true);
143-
} else {
144-
collectorContext.getEvaluatedItems().clear();
145-
collectorContext.getEvaluatedProperties().clear();
147+
grandParentScope.mergeWith(parentScope);
146148
}
147-
collectorContext.getEvaluatedItems().addAll(backupEvaluatedItems);
148-
collectorContext.getEvaluatedProperties().addAll(backupEvaluatedProperties);
149149
}
150150
return Collections.unmodifiableSet(allErrors);
151151
}

0 commit comments

Comments
 (0)