Skip to content

Commit 3013f3c

Browse files
Changes for adding walk capabilities
1 parent 6a3b90d commit 3013f3c

File tree

10 files changed

+285
-167
lines changed

10 files changed

+285
-167
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<modelVersion>4.0.0</modelVersion>
2121
<groupId>com.networknt</groupId>
2222
<artifactId>json-schema-validator</artifactId>
23-
<version>1.0.43</version>
23+
<version>1.0.43 </version>
2424
<packaging>bundle</packaging>
2525
<description>A json schema validator that supports draft v4, v6, v7 and v2019-09</description>
2626
<url>https://github.com/networknt/json-schema-validator</url>

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

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@
2323
import java.util.HashMap;
2424
import java.util.Iterator;
2525
import java.util.LinkedHashSet;
26-
import java.util.List;
2726
import java.util.Map;
27+
import java.util.Map.Entry;
2828
import java.util.Set;
2929
import java.util.regex.Matcher;
3030
import java.util.regex.Pattern;
3131

3232
import com.fasterxml.jackson.databind.JsonNode;
33-
import com.networknt.schema.walk.KeywordWalkListener;
33+
import com.networknt.schema.walk.JsonWalker;
3434
import com.networknt.schema.walk.KeywordWalkListenerRunner;
3535

3636
/**
@@ -43,8 +43,7 @@ public class JsonSchema extends BaseJsonValidator {
4343
protected Map<String, JsonValidator> validators;
4444
private final String idKeyword;
4545
private final ValidationContext validationContext;
46-
protected KeywordWalkListenerRunner jsonKeywordWalkListenerRunner;
47-
protected List<KeywordWalkListener> jsonKeywordWalkListeners;
46+
private KeywordWalkListenerRunner keywordWalkListenerRunner;
4847

4948
/**
5049
* This is the current uri of this schema. This uri could refer to the uri of this schema's file
@@ -80,8 +79,7 @@ private JsonSchema(ValidationContext validationContext, String schemaPath, URI c
8079
this.config = validationContext.getConfig();
8180
this.idKeyword = validationContext.getMetaSchema().getIdKeyword();
8281
this.currentUri = this.combineCurrentUriWithIds(currentUri, schemaNode);
83-
this.jsonKeywordWalkListeners = validationContext.getJsonKeywordWalkListeners();
84-
this.jsonKeywordWalkListenerRunner = new KeywordWalkListenerRunner(jsonKeywordWalkListeners);
82+
this.keywordWalkListenerRunner = new KeywordWalkListenerRunner(validationContext.getKeywordWalkListenersMap());
8583
}
8684

8785
JsonSchema initialize() {
@@ -285,21 +283,19 @@ public ValidationResult walk(JsonNode node, boolean shouldValidateSchema) {
285283
@Override
286284
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
287285
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();
286+
// Walk through all the JSONWalker's.
287+
for (Entry<String, JsonValidator> entry : validators.entrySet()) {
288+
JsonWalker jsonWalker = entry.getValue();
289+
String schemaPathWithKeyword = entry.getKey();
294290
try {
295291
// Call all the pre-walk listeners.
296-
jsonKeywordWalkListenerRunner.runPreWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
292+
keywordWalkListenerRunner.runPreWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
297293
schemaNode, parentSchema);
298-
validationMessages.addAll(jsonValidator.walk(node, rootNode, AT_ROOT, shouldValidateSchema));
294+
validationMessages.addAll(jsonWalker.walk(node, rootNode, AT_ROOT, shouldValidateSchema));
299295
} finally {
300296
// Call all the post-walk listeners.
301-
jsonKeywordWalkListenerRunner.runPostWalkListeners(schemaPathWithKeyword, node, rootNode, at,
302-
schemaPath, schemaNode, parentSchema, validationMessages);
297+
keywordWalkListenerRunner.runPostWalkListeners(schemaPathWithKeyword, node, rootNode, at, schemaPath,
298+
schemaNode, parentSchema, validationMessages);
303299
}
304300
}
305301
return validationMessages;

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

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
public class JsonSchemaFactory {
4848
private static final Logger logger = LoggerFactory
4949
.getLogger(JsonSchemaFactory.class);
50+
// This is just a constant for listening to all Keywords.
51+
public static final String ALL_KEYWORD_WALK_LISTENER_KEY = "com.networknt.AllKeywordWalkListener";
5052

5153

5254
public static class Builder {
@@ -57,7 +59,7 @@ public static class Builder {
5759
private URNFactory urnFactory;
5860
private final Map<String, JsonMetaSchema> jsonMetaSchemas = new HashMap<String, JsonMetaSchema>();
5961
private final Map<String, String> uriMap = new HashMap<String, String>();
60-
private final List<KeywordWalkListener> jsonKeywordWalkListeners = new ArrayList<KeywordWalkListener>();
62+
private final Map<String, List<KeywordWalkListener>> keywordWalkListenersMap = new HashMap<String, List<KeywordWalkListener>>();
6163

6264
public Builder() {
6365
// Adds support for creating {@link URL}s.
@@ -141,15 +143,42 @@ public Builder addUrnFactory(URNFactory urnFactory) {
141143
return this;
142144
}
143145

144-
public Builder addKeywordWalkListener(KeywordWalkListener jsonKeywordWalkListener) {
145-
this.jsonKeywordWalkListeners.add(jsonKeywordWalkListener);
146-
return this;
147-
}
146+
public Builder addKeywordWalkListener(KeywordWalkListener keywordWalkListener) {
147+
if (keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY) == null) {
148+
List<KeywordWalkListener> keywordWalkListeners = new ArrayList<KeywordWalkListener>();
149+
keywordWalkListenersMap.put(ALL_KEYWORD_WALK_LISTENER_KEY, keywordWalkListeners);
150+
}
151+
keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY).add(keywordWalkListener);
152+
return this;
153+
}
154+
155+
public Builder addKeywordWalkListener(String keyword, KeywordWalkListener keywordWalkListener) {
156+
if (keywordWalkListenersMap.get(keyword) == null) {
157+
List<KeywordWalkListener> keywordWalkListeners = new ArrayList<KeywordWalkListener>();
158+
keywordWalkListenersMap.put(keyword, keywordWalkListeners);
159+
}
160+
keywordWalkListenersMap.get(keyword).add(keywordWalkListener);
161+
return this;
162+
}
148163

149-
public Builder addKeywordWalkListeners(Collection<KeywordWalkListener> jsonKeywordWalkListener) {
150-
this.jsonKeywordWalkListeners.addAll(jsonKeywordWalkListener);
151-
return this;
152-
}
164+
165+
public Builder addKeywordWalkListeners(List<KeywordWalkListener> keywordWalkListeners) {
166+
if (keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY) == null) {
167+
List<KeywordWalkListener> ikeywordWalkListeners = new ArrayList<KeywordWalkListener>();
168+
keywordWalkListenersMap.put(ALL_KEYWORD_WALK_LISTENER_KEY, ikeywordWalkListeners);
169+
}
170+
keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY).addAll(keywordWalkListeners);
171+
return this;
172+
}
173+
174+
public Builder addKeywordWalkListeners(String keyword, List<KeywordWalkListener> keywordWalkListeners) {
175+
if (keywordWalkListenersMap.get(keyword) == null) {
176+
List<KeywordWalkListener> ikeywordWalkListeners = new ArrayList<KeywordWalkListener>();
177+
keywordWalkListenersMap.put(keyword, ikeywordWalkListeners);
178+
}
179+
keywordWalkListenersMap.get(keyword).addAll(keywordWalkListeners);
180+
return this;
181+
}
153182

154183
public JsonSchemaFactory build() {
155184
// create builtin keywords with (custom) formats.
@@ -161,7 +190,7 @@ public JsonSchemaFactory build() {
161190
urnFactory,
162191
jsonMetaSchemas,
163192
uriMap,
164-
jsonKeywordWalkListeners
193+
keywordWalkListenersMap
165194
);
166195
}
167196
}
@@ -174,7 +203,7 @@ public JsonSchemaFactory build() {
174203
private final Map<String, JsonMetaSchema> jsonMetaSchemas;
175204
private final Map<String, String> uriMap;
176205
private final ConcurrentMap<URI, JsonSchema> uriSchemaCache = new ConcurrentHashMap<URI, JsonSchema>();
177-
private List<KeywordWalkListener> jsonKeywordWalkListeners = new ArrayList<KeywordWalkListener>();
206+
private final Map<String, List<KeywordWalkListener>> keywordWalkListenersMap;
178207

179208

180209
private JsonSchemaFactory(
@@ -185,7 +214,7 @@ private JsonSchemaFactory(
185214
final URNFactory urnFactory,
186215
final Map<String, JsonMetaSchema> jsonMetaSchemas,
187216
final Map<String, String> uriMap,
188-
final List<KeywordWalkListener> jsonKeywordWalkListeners) {
217+
final Map<String, List<KeywordWalkListener>> keywordWalkListenersMap) {
189218
if (mapper == null) {
190219
throw new IllegalArgumentException("ObjectMapper must not be null");
191220
} else if (defaultMetaSchemaURI == null || defaultMetaSchemaURI.trim().isEmpty()) {
@@ -208,7 +237,7 @@ private JsonSchemaFactory(
208237
this.urnFactory = urnFactory;
209238
this.jsonMetaSchemas = jsonMetaSchemas;
210239
this.uriMap = uriMap;
211-
this.jsonKeywordWalkListeners = jsonKeywordWalkListeners;
240+
this.keywordWalkListenersMap = keywordWalkListenersMap;
212241
}
213242

214243
/**
@@ -282,7 +311,7 @@ protected JsonSchema newJsonSchema(final URI schemaUri, final JsonNode schemaNod
282311

283312
protected ValidationContext createValidationContext(final JsonNode schemaNode) {
284313
final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode);
285-
return new ValidationContext(this.uriFactory, this.urnFactory, jsonMetaSchema, this, null, this.jsonKeywordWalkListeners);
314+
return new ValidationContext(this.uriFactory, this.urnFactory, jsonMetaSchema, this, null, this.keywordWalkListenersMap);
286315
}
287316

288317
private JsonMetaSchema findMetaSchemaForSchema(final JsonNode schemaNode) {
@@ -354,7 +383,7 @@ public JsonSchema getSchema(final URI schemaUri, final SchemaValidatorsConfig co
354383

355384
JsonSchema jsonSchema;
356385
if (idMatchesSourceUri(jsonMetaSchema, schemaNode, schemaUri)) {
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*/);
386+
jsonSchema = new JsonSchema(new ValidationContext(this.uriFactory, this.urnFactory, jsonMetaSchema, this, config, this.keywordWalkListenersMap), mappedUri, schemaNode, true /*retrieved via id, resolving will not change anything*/);
358387
} else {
359388
final ValidationContext validationContext = createValidationContext(schemaNode);
360389
validationContext.setConfig(config);

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

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@
1616

1717
package com.networknt.schema;
1818

19-
import com.fasterxml.jackson.databind.JsonNode;
20-
21-
import java.util.List;
2219
import java.util.Set;
2320

21+
import com.fasterxml.jackson.databind.JsonNode;
22+
import com.networknt.schema.walk.JsonWalker;
23+
2424
/**
2525
* Standard json validator interface, implemented by all validators and JsonSchema.
2626
*/
27-
public interface JsonValidator {
27+
public interface JsonValidator extends JsonWalker {
2828
public static final String AT_ROOT = "$";
2929

3030

@@ -49,25 +49,5 @@ public interface JsonValidator {
4949
*/
5050
Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at);
5151

52-
/**
53-
*
54-
* This method gives the capability to walk through the given JsonNode, allowing
55-
* functionality beyond validation like collecting information,handling cross
56-
* cutting concerns like logging or instrumentation. This method also performs
57-
* the validation if {@code shouldValidateSchema} is set to true. <br>
58-
* <br>
59-
* {@link BaseJsonValidator#walk(JsonNode, JsonNode, String, List, boolean)}
60-
* provides a default implementation of this method. However keywords that parse
61-
* sub-schemas should override this method to call walk method on those
62-
* subschemas.
63-
*
64-
* @param node
65-
* @param rootNode
66-
* @param at
67-
* @param jsonWalkListeners
68-
* @param shouldValidateSchema
69-
* @return
70-
*/
71-
Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema);
7252

7353
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ public class ValidationContext {
3232
private final JsonSchemaFactory jsonSchemaFactory;
3333
private SchemaValidatorsConfig config;
3434
private final Map<String, JsonSchemaRef> refParsingInProgress = new HashMap<String, JsonSchemaRef>();
35-
private List<KeywordWalkListener> jsonKeywordWalkListeners;
35+
private final Map<String, List<KeywordWalkListener>> keywordWalkListenersMap;
3636

3737
public ValidationContext(URIFactory uriFactory, URNFactory urnFactory, JsonMetaSchema metaSchema,
3838
JsonSchemaFactory jsonSchemaFactory, SchemaValidatorsConfig config,
39-
List<KeywordWalkListener> jsonKeywordWalkListeners) {
39+
Map<String, List<KeywordWalkListener>> keywordWalkListenersMap) {
4040
if (uriFactory == null) {
4141
throw new IllegalArgumentException("URIFactory must not be null");
4242
}
@@ -51,7 +51,7 @@ public ValidationContext(URIFactory uriFactory, URNFactory urnFactory, JsonMetaS
5151
this.metaSchema = metaSchema;
5252
this.jsonSchemaFactory = jsonSchemaFactory;
5353
this.config = config;
54-
this.jsonKeywordWalkListeners = jsonKeywordWalkListeners;
54+
this.keywordWalkListenersMap = keywordWalkListenersMap;
5555
}
5656

5757
public JsonValidator newValidator(String schemaPath, String keyword /* keyword */, JsonNode schemaNode,
@@ -95,8 +95,8 @@ protected JsonMetaSchema getMetaSchema() {
9595
return metaSchema;
9696
}
9797

98-
public List<KeywordWalkListener> getJsonKeywordWalkListeners() {
99-
return jsonKeywordWalkListeners;
98+
public Map<String, List<KeywordWalkListener>> getKeywordWalkListenersMap() {
99+
return keywordWalkListenersMap;
100100
}
101101

102102
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.networknt.schema.walk;
2+
3+
import java.util.Set;
4+
5+
import com.fasterxml.jackson.databind.JsonNode;
6+
import com.networknt.schema.BaseJsonValidator;
7+
import com.networknt.schema.ValidationMessage;
8+
9+
public interface JsonWalker {
10+
/**
11+
*
12+
* This method gives the capability to walk through the given JsonNode, allowing
13+
* functionality beyond validation like collecting information,handling cross
14+
* cutting concerns like logging or instrumentation. This method also performs
15+
* the validation if {@code shouldValidateSchema} is set to true. <br>
16+
* <br>
17+
* {@link BaseJsonValidator#walk(JsonNode, JsonNode, String, boolean)} provides
18+
* a default implementation of this method. However keywords that parse
19+
* sub-schemas should override this method to call walk method on those
20+
* subschemas.
21+
*
22+
* @param node JsonNode
23+
* @param rootNode JsonNode
24+
* @param at String
25+
* @param shouldValidateSchema boolean
26+
* @return a set of validation messages if shouldValidateSchema is true.
27+
*/
28+
Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema);
29+
}

src/main/java/com/networknt/schema/walk/KeywordWalkListenerRunner.java

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,63 @@
11
package com.networknt.schema.walk;
22

3-
import java.util.Collection;
3+
import java.util.List;
4+
import java.util.Map;
45
import java.util.Set;
56

67
import com.fasterxml.jackson.databind.JsonNode;
78
import com.networknt.schema.JsonSchema;
9+
import com.networknt.schema.JsonSchemaFactory;
810
import com.networknt.schema.ValidationMessage;
911

1012
public class KeywordWalkListenerRunner {
1113

12-
private Collection<KeywordWalkListener> jsonKeywordWalkListeners;
14+
private Map<String, List<KeywordWalkListener>> keywordWalkListenersMap;
1315

14-
public KeywordWalkListenerRunner(Collection<KeywordWalkListener> jsonKeywordWalkListeners) {
15-
this.jsonKeywordWalkListeners = jsonKeywordWalkListeners;
16+
public KeywordWalkListenerRunner(Map<String, List<KeywordWalkListener>> keywordWalkListenersMap) {
17+
this.keywordWalkListenersMap = keywordWalkListenersMap;
1618
}
1719

1820
public void runPreWalkListeners(String keyWordPath, JsonNode node, JsonNode rootNode, String at, String schemaPath,
1921
JsonNode schemaNode, JsonSchema parentSchema) {
20-
KeywordWalkEvent keywordWalkEvent = constructKeywordWalkEvent(getKeywordName(keyWordPath), node, rootNode, at,
21-
schemaPath, schemaNode, parentSchema);
22-
for (KeywordWalkListener jsonKeywordWalkListener : jsonKeywordWalkListeners) {
23-
jsonKeywordWalkListener.onWalkStart(keywordWalkEvent);
22+
String keyword = getKeywordName(keyWordPath);
23+
KeywordWalkEvent keywordWalkEvent = constructKeywordWalkEvent(keyword, node, rootNode, at, schemaPath,
24+
schemaNode, parentSchema);
25+
List<KeywordWalkListener> allKeywordListeners = keywordWalkListenersMap
26+
.get(JsonSchemaFactory.ALL_KEYWORD_WALK_LISTENER_KEY);
27+
// Run Listeners that are setup for all keywords.
28+
if (allKeywordListeners != null) {
29+
for (KeywordWalkListener jsonKeywordWalkListener : allKeywordListeners) {
30+
jsonKeywordWalkListener.onWalkStart(keywordWalkEvent);
31+
}
32+
}
33+
// Run Listeners that are setup only for this keyword.
34+
List<KeywordWalkListener> currentKeywordListeners = keywordWalkListenersMap.get(keyword);
35+
if (currentKeywordListeners != null) {
36+
for (KeywordWalkListener jsonKeywordWalkListener : currentKeywordListeners) {
37+
jsonKeywordWalkListener.onWalkStart(keywordWalkEvent);
38+
}
2439
}
2540
}
2641

2742
public void runPostWalkListeners(String keyWordPath, JsonNode node, JsonNode rootNode, String at, String schemaPath,
2843
JsonNode schemaNode, JsonSchema parentSchema, Set<ValidationMessage> validationMessages) {
29-
KeywordWalkEvent keywordWalkEvent = constructKeywordWalkEvent(getKeywordName(keyWordPath), node, rootNode, at,
30-
schemaPath, schemaNode, parentSchema);
31-
for (KeywordWalkListener jsonKeywordWalkListener : jsonKeywordWalkListeners) {
32-
jsonKeywordWalkListener.onWalkEnd(keywordWalkEvent, validationMessages);
44+
String keyword = getKeywordName(keyWordPath);
45+
KeywordWalkEvent keywordWalkEvent = constructKeywordWalkEvent(keyword, node, rootNode, at, schemaPath,
46+
schemaNode, parentSchema);
47+
List<KeywordWalkListener> allKeywordListeners = keywordWalkListenersMap
48+
.get(JsonSchemaFactory.ALL_KEYWORD_WALK_LISTENER_KEY);
49+
// Run Listeners that are setup for all keywords.
50+
if (allKeywordListeners != null) {
51+
for (KeywordWalkListener jsonKeywordWalkListener : allKeywordListeners) {
52+
jsonKeywordWalkListener.onWalkEnd(keywordWalkEvent, validationMessages);
53+
}
54+
}
55+
// Run Listeners that are setup only for this keyword.
56+
List<KeywordWalkListener> currentKeywordListeners = keywordWalkListenersMap.get(keyword);
57+
if (currentKeywordListeners != null) {
58+
for (KeywordWalkListener jsonKeywordWalkListener : currentKeywordListeners) {
59+
jsonKeywordWalkListener.onWalkEnd(keywordWalkEvent, validationMessages);
60+
}
3361
}
3462
}
3563

0 commit comments

Comments
 (0)