Skip to content

Commit 8f42eac

Browse files
Adding walk changes to applicable validators
1 parent e41595a commit 8f42eac

18 files changed

+615
-42
lines changed

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616

1717
package com.networknt.schema;
1818

19-
import com.fasterxml.jackson.databind.JsonNode;
20-
2119
import java.util.Collections;
2220
import java.util.Map;
2321
import java.util.Set;
2422

23+
import com.fasterxml.jackson.databind.JsonNode;
24+
2525
public abstract class AbstractJsonValidator implements JsonValidator {
2626
private final String keyword;
2727

@@ -52,4 +52,13 @@ protected Set<ValidationMessage> fail(ErrorMessageType errorMessageType, String
5252
protected Set<ValidationMessage> fail(ErrorMessageType errorMessageType, String at, String... arguments) {
5353
return Collections.singleton(buildValidationMessage(errorMessageType, at, arguments));
5454
}
55+
56+
@Override
57+
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
58+
Set<ValidationMessage> validationMessages = Collections.emptySet();
59+
if (shouldValidateSchema) {
60+
validationMessages = validate(node, rootNode, at);
61+
}
62+
return validationMessages;
63+
}
5564
}

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,5 +101,42 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
101101
}
102102
return Collections.unmodifiableSet(errors);
103103
}
104+
105+
@Override
106+
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
107+
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
108+
if (!node.isObject()) {
109+
return validationMessages;
110+
}
111+
// Validate schema if required.
112+
if (shouldValidateSchema) {
113+
return validate(node, rootNode, at);
114+
}
115+
for (Iterator<String> it = node.fieldNames(); it.hasNext();) {
116+
String pname = it.next();
117+
// skip the context items
118+
if (pname.startsWith("#")) {
119+
continue;
120+
}
121+
boolean handledByPatternProperties = false;
122+
for (Pattern pattern : patternProperties) {
123+
Matcher m = pattern.matcher(pname);
124+
if (m.find()) {
125+
handledByPatternProperties = true;
126+
break;
127+
}
128+
}
129+
if (!allowedProperties.contains(pname) && !handledByPatternProperties) {
130+
if (allowAdditionalProperties) {
131+
if (additionalPropertiesSchema != null) {
132+
additionalPropertiesSchema.walk(node.get(pname), rootNode, at + "." + pname,
133+
shouldValidateSchema);
134+
}
135+
}
136+
}
137+
}
138+
139+
return validationMessages;
140+
}
104141

105142
}

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class AllOfValidator extends BaseJsonValidator implements JsonValidator {
2626
private static final Logger logger = LoggerFactory.getLogger(AllOfValidator.class);
2727

2828
private List<JsonSchema> schemas = new ArrayList<JsonSchema>();
29-
29+
3030
public AllOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
3131
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ALL_OF, validationContext);
3232
int size = schemaNode.size();
@@ -47,5 +47,17 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
4747

4848
return Collections.unmodifiableSet(errors);
4949
}
50+
51+
@Override
52+
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
53+
// Check if validation is needed. Else just walk through the schemas.
54+
if (shouldValidateSchema) {
55+
return validate(node, rootNode, at);
56+
}
57+
for (JsonSchema schema : schemas) {
58+
schema.walk(node, rootNode, at, shouldValidateSchema);
59+
}
60+
return new LinkedHashSet<ValidationMessage>();
61+
}
5062

5163
}

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

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,19 @@
1616

1717
package com.networknt.schema;
1818

19-
import com.fasterxml.jackson.databind.JsonNode;
19+
import java.net.URI;
20+
import java.util.LinkedHashSet;
21+
import java.util.Set;
22+
2023
import org.apache.commons.lang3.StringUtils;
2124
import org.slf4j.Logger;
2225

23-
import java.net.URI;
24-
import java.util.Set;
26+
import com.fasterxml.jackson.databind.JsonNode;
2527

2628
public abstract class BaseJsonValidator implements JsonValidator {
27-
private String schemaPath;
28-
private JsonNode schemaNode;
29-
private JsonSchema parentSchema;
29+
protected String schemaPath;
30+
protected JsonNode schemaNode;
31+
protected JsonSchema parentSchema;
3032
private boolean suppressSubSchemaRetrieval;
3133
private ValidatorTypeCode validatorType;
3234
private ErrorMessageType errorMessageType;
@@ -148,4 +150,18 @@ protected String getNodeFieldType() {
148150
}
149151
return null;
150152
}
153+
154+
/**
155+
* This is default implementation of walk method. Its job is to call the
156+
* validate method if shouldValidateSchema is enabled.
157+
*/
158+
@Override
159+
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
160+
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
161+
if (shouldValidateSchema) {
162+
validationMessages = validate(node, rootNode, at);
163+
}
164+
return validationMessages;
165+
}
166+
151167
}

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

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,23 @@
1616

1717
package com.networknt.schema;
1818

19-
import com.fasterxml.jackson.databind.JsonNode;
20-
2119
import java.io.UnsupportedEncodingException;
2220
import java.net.URI;
2321
import java.net.URLDecoder;
24-
import java.util.*;
22+
import java.util.Collections;
23+
import java.util.HashMap;
24+
import java.util.Iterator;
25+
import java.util.LinkedHashSet;
26+
import java.util.List;
27+
import java.util.Map;
28+
import java.util.Set;
2529
import java.util.regex.Matcher;
2630
import java.util.regex.Pattern;
2731

32+
import com.fasterxml.jackson.databind.JsonNode;
33+
import com.networknt.schema.walk.KeywordWalkListener;
34+
import com.networknt.schema.walk.KeywordWalkListenerRunner;
35+
2836
/**
2937
* This is the core of json constraint implementation. It parses json constraint
3038
* file and generates JsonValidators. The class is thread safe, once it is
@@ -35,6 +43,8 @@ public class JsonSchema extends BaseJsonValidator {
3543
protected Map<String, JsonValidator> validators;
3644
private final String idKeyword;
3745
private final ValidationContext validationContext;
46+
protected KeywordWalkListenerRunner jsonKeywordWalkListenerRunner;
47+
protected List<KeywordWalkListener> jsonKeywordWalkListeners;
3848

3949
/**
4050
* This is the current uri of this schema. This uri could refer to the uri of this schema's file
@@ -70,6 +80,8 @@ private JsonSchema(ValidationContext validationContext, String schemaPath, URI c
7080
this.config = validationContext.getConfig();
7181
this.idKeyword = validationContext.getMetaSchema().getIdKeyword();
7282
this.currentUri = this.combineCurrentUriWithIds(currentUri, schemaNode);
83+
this.jsonKeywordWalkListeners = validationContext.getJsonKeywordWalkListeners();
84+
this.jsonKeywordWalkListenerRunner = new KeywordWalkListenerRunner(jsonKeywordWalkListeners);
7385
}
7486

7587
JsonSchema initialize() {
@@ -179,6 +191,11 @@ private boolean nodeContainsRef(String ref, JsonNode node) {
179191
return false;
180192
}
181193

194+
195+
/**
196+
* Please note that the key in {@link #validators} map is a schema path. It is
197+
* used in {@link KeywordWalkListenerRunner} to derive the keyword.
198+
*/
182199
private Map<String, JsonValidator> read(JsonNode schemaNode) {
183200
Map<String, JsonValidator> validators = new HashMap<String, JsonValidator>();
184201
if (schemaNode.isBoolean()) {
@@ -207,6 +224,8 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {
207224
}
208225
return validators;
209226
}
227+
228+
/************************ START OF VALIDATE METHODS **********************************/
210229

211230
public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, String at) {
212231
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
@@ -245,6 +264,48 @@ protected ValidationResult validateAndCollect(JsonNode jsonNode, JsonNode rootNo
245264
ThreadInfo.remove(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY);
246265
}
247266
}
267+
268+
/************************ END OF VALIDATE METHODS **********************************/
269+
270+
/************************ START OF WALK METHODS **********************************/
271+
272+
public ValidationResult walk(JsonNode node, boolean shouldValidateSchema) {
273+
// Create the collector context object.
274+
CollectorContext collectorContext = new CollectorContext();
275+
// Set the collector context in thread info, this is unique for every thread.
276+
ThreadInfo.set(CollectorContext.COLLECTOR_CONTEXT_THREAD_LOCAL_KEY, collectorContext);
277+
Set<ValidationMessage> errors = walk(node, node, AT_ROOT, shouldValidateSchema);
278+
// Load all the data from collectors into the context.
279+
collectorContext.loadCollectors();
280+
// Collect errors and collector context into validation result.
281+
ValidationResult validationResult = new ValidationResult(errors, collectorContext);
282+
return validationResult;
283+
}
284+
285+
@Override
286+
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
287+
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
288+
Iterator<String> schemaPathWithKeywordIterator = validators.keySet().iterator();
289+
Iterator<JsonValidator> validatorsIterator = validators.values().iterator();
290+
// Walk through all the validator's.
291+
while (schemaPathWithKeywordIterator.hasNext() && validatorsIterator.hasNext()) {
292+
JsonValidator jsonValidator = validatorsIterator.next();
293+
String schemaPathWithKeyword = schemaPathWithKeywordIterator.next();
294+
try {
295+
// Call all the pre-walk listeners.
296+
jsonKeywordWalkListenerRunner.runPreWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
297+
schemaNode, parentSchema);
298+
validationMessages.addAll(jsonValidator.walk(node, rootNode, AT_ROOT, shouldValidateSchema));
299+
} finally {
300+
// Call all the post-walk listeners.
301+
jsonKeywordWalkListenerRunner.runPostWalkListeners(schemaPathWithKeyword, node, rootNode, at,
302+
schemaPath, schemaNode, parentSchema, validationMessages);
303+
}
304+
}
305+
return validationMessages;
306+
}
307+
308+
/************************ END OF WALK METHODS **********************************/
248309

249310
@Override
250311
public String toString() {

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

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,34 @@
1616

1717
package com.networknt.schema;
1818

19-
import com.fasterxml.jackson.databind.JsonNode;
20-
import com.fasterxml.jackson.databind.ObjectMapper;
21-
import com.networknt.schema.uri.*;
22-
import com.networknt.schema.urn.URNFactory;
23-
import org.slf4j.Logger;
24-
import org.slf4j.LoggerFactory;
25-
2619
import java.io.IOException;
2720
import java.io.InputStream;
2821
import java.net.URI;
2922
import java.net.URISyntaxException;
23+
import java.util.ArrayList;
3024
import java.util.Collection;
3125
import java.util.HashMap;
26+
import java.util.List;
3227
import java.util.Map;
3328
import java.util.concurrent.ConcurrentHashMap;
3429
import java.util.concurrent.ConcurrentMap;
3530

31+
import org.slf4j.Logger;
32+
import org.slf4j.LoggerFactory;
33+
34+
import com.fasterxml.jackson.databind.JsonNode;
35+
import com.fasterxml.jackson.databind.ObjectMapper;
36+
import com.networknt.schema.uri.ClasspathURLFactory;
37+
import com.networknt.schema.uri.ClasspathURLFetcher;
38+
import com.networknt.schema.uri.URIFactory;
39+
import com.networknt.schema.uri.URIFetcher;
40+
import com.networknt.schema.uri.URISchemeFactory;
41+
import com.networknt.schema.uri.URISchemeFetcher;
42+
import com.networknt.schema.uri.URLFactory;
43+
import com.networknt.schema.uri.URLFetcher;
44+
import com.networknt.schema.urn.URNFactory;
45+
import com.networknt.schema.walk.KeywordWalkListener;
46+
3647
public class JsonSchemaFactory {
3748
private static final Logger logger = LoggerFactory
3849
.getLogger(JsonSchemaFactory.class);
@@ -46,6 +57,7 @@ public static class Builder {
4657
private URNFactory urnFactory;
4758
private final Map<String, JsonMetaSchema> jsonMetaSchemas = new HashMap<String, JsonMetaSchema>();
4859
private final Map<String, String> uriMap = new HashMap<String, String>();
60+
private final List<KeywordWalkListener> jsonKeywordWalkListeners = new ArrayList<KeywordWalkListener>();
4961

5062
public Builder() {
5163
// Adds support for creating {@link URL}s.
@@ -128,6 +140,16 @@ public Builder addUrnFactory(URNFactory urnFactory) {
128140
this.urnFactory = urnFactory;
129141
return this;
130142
}
143+
144+
public Builder addKeywordWalkListener(KeywordWalkListener jsonKeywordWalkListener) {
145+
this.jsonKeywordWalkListeners.add(jsonKeywordWalkListener);
146+
return this;
147+
}
148+
149+
public Builder addKeywordWalkListeners(Collection<KeywordWalkListener> jsonKeywordWalkListener) {
150+
this.jsonKeywordWalkListeners.addAll(jsonKeywordWalkListener);
151+
return this;
152+
}
131153

132154
public JsonSchemaFactory build() {
133155
// create builtin keywords with (custom) formats.
@@ -138,7 +160,8 @@ public JsonSchemaFactory build() {
138160
new URISchemeFetcher(uriFetcherMap),
139161
urnFactory,
140162
jsonMetaSchemas,
141-
uriMap
163+
uriMap,
164+
jsonKeywordWalkListeners
142165
);
143166
}
144167
}
@@ -151,6 +174,7 @@ public JsonSchemaFactory build() {
151174
private final Map<String, JsonMetaSchema> jsonMetaSchemas;
152175
private final Map<String, String> uriMap;
153176
private final ConcurrentMap<URI, JsonSchema> uriSchemaCache = new ConcurrentHashMap<URI, JsonSchema>();
177+
private List<KeywordWalkListener> jsonKeywordWalkListeners = new ArrayList<KeywordWalkListener>();
154178

155179

156180
private JsonSchemaFactory(
@@ -160,7 +184,8 @@ private JsonSchemaFactory(
160184
final URISchemeFetcher uriFetcher,
161185
final URNFactory urnFactory,
162186
final Map<String, JsonMetaSchema> jsonMetaSchemas,
163-
final Map<String, String> uriMap) {
187+
final Map<String, String> uriMap,
188+
final List<KeywordWalkListener> jsonKeywordWalkListeners) {
164189
if (mapper == null) {
165190
throw new IllegalArgumentException("ObjectMapper must not be null");
166191
} else if (defaultMetaSchemaURI == null || defaultMetaSchemaURI.trim().isEmpty()) {
@@ -183,6 +208,7 @@ private JsonSchemaFactory(
183208
this.urnFactory = urnFactory;
184209
this.jsonMetaSchemas = jsonMetaSchemas;
185210
this.uriMap = uriMap;
211+
this.jsonKeywordWalkListeners = jsonKeywordWalkListeners;
186212
}
187213

188214
/**
@@ -256,7 +282,7 @@ protected JsonSchema newJsonSchema(final URI schemaUri, final JsonNode schemaNod
256282

257283
protected ValidationContext createValidationContext(final JsonNode schemaNode) {
258284
final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode);
259-
return new ValidationContext(this.uriFactory, this.urnFactory, jsonMetaSchema, this, null);
285+
return new ValidationContext(this.uriFactory, this.urnFactory, jsonMetaSchema, this, null, this.jsonKeywordWalkListeners);
260286
}
261287

262288
private JsonMetaSchema findMetaSchemaForSchema(final JsonNode schemaNode) {
@@ -328,7 +354,7 @@ public JsonSchema getSchema(final URI schemaUri, final SchemaValidatorsConfig co
328354

329355
JsonSchema jsonSchema;
330356
if (idMatchesSourceUri(jsonMetaSchema, schemaNode, schemaUri)) {
331-
jsonSchema = new JsonSchema(new ValidationContext(this.uriFactory, this.urnFactory, jsonMetaSchema, this, config), mappedUri, schemaNode, true /*retrieved via id, resolving will not change anything*/);
357+
jsonSchema = new JsonSchema(new ValidationContext(this.uriFactory, this.urnFactory, jsonMetaSchema, this, config, this.jsonKeywordWalkListeners), mappedUri, schemaNode, true /*retrieved via id, resolving will not change anything*/);
332358
} else {
333359
final ValidationContext validationContext = createValidationContext(schemaNode);
334360
validationContext.setConfig(config);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,8 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
5252
public JsonSchema getSchema() {
5353
return schema;
5454
}
55+
56+
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
57+
return schema.walk(node, rootNode, at, shouldValidateSchema);
58+
}
5559
}

0 commit comments

Comments
 (0)