Skip to content

Commit a04a1fa

Browse files
Walk Changes
1 parent 3c675e6 commit a04a1fa

12 files changed

+122
-67
lines changed

doc/walk_flow.png

37.8 KB
Loading

doc/walkers.md

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
There can be use-cases where we need the capability to walk through the given JsonNode allowing functionality beyond validation like collecting information,handling cross cutting concerns like logging or instrumentation. JSON walkers were introduced to complement the validation functionality this library already provides.
44

5-
Currently walking is defined at the validator instance level for all the built-in keywords.
5+
Currently, walking is defined at the validator instance level for all the built-in keywords.
66

77
### Walk methods
88

99
A new interface is introduced into the library that a Walker should implement. It should be noted that this interface also allows the validation based on shouldValidateSchema parameter.
1010

11-
```
12-
public interface JsonWalker {
11+
```java
12+
public interface JsonSchemaWalker {
1313
/**
1414
*
1515
* This method gives the capability to walk through the given JsonNode, allowing
@@ -33,9 +33,9 @@ public interface JsonWalker {
3333

3434
```
3535

36-
The JSONValidator interface extends this new interface thus allowing all the validator's defined in library to implement this new interface. A default implementation of the walk method is provided in BaseJsonValidator class. In this case the walk method does nothing but validating based on shouldValidateSchema parameter.
36+
The JSONValidator interface extends this new interface thus allowing all the validator's defined in library to implement this new interface. BaseJsonValidator class provides a default implementation of the walk method. In this case the walk method does nothing but validating based on shouldValidateSchema parameter.
3737

38-
```
38+
```java
3939
/**
4040
* This is default implementation of walk method. Its job is to call the
4141
* validate method if shouldValidateSchema is enabled.
@@ -51,9 +51,9 @@ The JSONValidator interface extends this new interface thus allowing all the val
5151

5252
```
5353

54-
A new walk method is introduced into JSONSchema class that allows us to walk through the JSONSchema.
54+
A new walk method added to the JSONSchema class allows us to walk through the JSONSchema.
5555

56-
```
56+
```java
5757
public ValidationResult walk(JsonNode node, boolean shouldValidateSchema) {
5858
// Create the collector context object.
5959
CollectorContext collectorContext = new CollectorContext();
@@ -97,46 +97,46 @@ ValidationResult result = jsonSchema.walk(data,false);
9797
9898
```
9999

100-
walk method can be overridden for select validator's based on the use-case. Currently walk method has been overridden in PropertiesValidator,ItemsValidator,AllOfValidator,NotValidator,PatternValidator,RefValidator,AdditionalPropertiesValidator to accommodate the walk logic of the enclosed schema's.
100+
walk method can be overridden for select validator's based on the use-case. Currently, walk method has been overridden in PropertiesValidator,ItemsValidator,AllOfValidator,NotValidator,PatternValidator,RefValidator,AdditionalPropertiesValidator to accommodate the walk logic of the enclosed schema's.
101101

102102
### Walk Listeners
103103

104-
Walk listeners allows to execute a custom logic before and after a JsonWalker walk method is called. Walk listeners are modeled by a WalkListener interface.
104+
Walk listeners allows to execute a custom logic before and after the invocation of a JsonWalker walk method. Walk listeners can be modeled by a WalkListener interface.
105105

106-
```
107-
public interface WalkListener {
106+
```java
107+
public interface JsonSchemaWalkListener {
108108

109-
public boolean onWalkStart(WalkEvent walkEvent);
109+
public WalkFlow onWalkStart(WalkEvent walkEvent);
110110

111-
public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages);
111+
public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages);
112112
}
113113
```
114114

115115
Following is the example of a sample WalkListener implementation.
116116

117-
```
118-
private class PropertiesKeywordListener implements WalkListener {
117+
```java
118+
private static class PropertiesKeywordListener implements JsonSchemaWalkListener {
119119

120120
@Override
121-
public boolean onWalkStart(WalkEvent keywordWalkEvent) {
121+
public WalkFlow onWalkStart(WalkEvent keywordWalkEvent) {
122122
JsonNode schemaNode = keywordWalkEvent.getSchemaNode();
123-
if(schemaNode.get("title").textValue().equals("Property3")) {
124-
return false;
123+
if (schemaNode.get("title").textValue().equals("Property3")) {
124+
return WalkFlow.SKIP;
125125
}
126-
return true;
126+
return WalkFlow.CONTINUE;
127127
}
128128

129129
@Override
130130
public void onWalkEnd(WalkEvent keywordWalkEvent, Set<ValidationMessage> validationMessages) {
131-
131+
132132
}
133133
}
134134
```
135-
If the onWalkStart method returns false, the actual walk method execution is skipped.
135+
If the onWalkStart method returns WalkFlow.SKIP, the actual walk method execution will be skipped.
136136

137137
Walk listeners can be added by using the SchemaValidatorsConfig class.
138138

139-
```
139+
```java
140140
SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig();
141141
schemaValidatorsConfig.addKeywordWalkListener(new AllKeywordListener());
142142
schemaValidatorsConfig.addKeywordWalkListener(ValidatorTypeCode.REF.getValue(), new RefKeywordListener());
@@ -149,13 +149,13 @@ this.jsonSchema = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig);
149149

150150
```
151151

152-
There are two kinds of walk listeners, keyword walk listeners and property walk listeners. Keyword walk listeners are called whenever the given keyword is encountered while walking the schema and JSON node data, for example we have added Ref and Property keyword walk listeners in the above example. Property walk listeners are called for every property defined in the JSON node data.
152+
There are two kinds of walk listeners, keyword walk listeners and property walk listeners. Keyword walk listeners will be called whenever the given keyword is encountered while walking the schema and JSON node data, for example we have added Ref and Property keyword walk listeners in the above example. Property walk listeners are called for every property defined in the JSON node data.
153153

154-
Both property walk listeners and keyword walk listener are modeled by using the same WalkListener interface. Following is an example of how to add a property walk listener.
154+
Both property walk listeners and keyword walk listener can be modeled by using the same WalkListener interface. Following is an example of how to add a property walk listener.
155155

156-
```
156+
```java
157157
SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig();
158-
schemaValidatorsConfig.addPropertyWalkListener(new ExampleProperties());
158+
schemaValidatorsConfig.addPropertyWalkListener(new ExamplePropertyWalkListener());
159159
final JsonSchemaFactory schemaFactory = JsonSchemaFactory
160160
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).addMetaSchema(metaSchema)
161161
.build();
@@ -171,7 +171,7 @@ A WalkEvent instance captures several details about the node currently being wal
171171

172172
Following snippet shows the details captured by WalkEvent instance.
173173

174-
```
174+
```java
175175
public class WalkEvent {
176176

177177
private String schemaPath;
@@ -182,4 +182,59 @@ public class WalkEvent {
182182
private JsonNode rootNode;
183183
private String at;
184184

185-
```
185+
```
186+
187+
### Sample Flow
188+
189+
Given an example schema as shown, if we write a property listener, the walk flow is as depicted in the image.
190+
191+
```json
192+
{
193+
194+
"title": "Sample Schema",
195+
"definitions" : {
196+
"address" :{
197+
"street-address": {
198+
"title": "Street Address",
199+
"type": "string"
200+
},
201+
"pincode": {
202+
"title": "Body",
203+
"type": "integer"
204+
}
205+
}
206+
},
207+
"properties": {
208+
"name": {
209+
"title": "Title",
210+
"type": "string",
211+
"maxLength": 50
212+
},
213+
"body": {
214+
"title": "Body",
215+
"type": "string"
216+
},
217+
"address": {
218+
"title": "Excerpt",
219+
"$ref": "#/definitions/address"
220+
}
221+
222+
},
223+
"additionalProperties": false
224+
}
225+
```
226+
227+
![img](walk_flow.png)<!-- .element height="50%" width="50%" -->
228+
229+
230+
Few important points to note about the flow.
231+
232+
1. onWalkStart and onWalkEnd are the methods defined in the property walk listener
233+
2. Anywhere during the flow, onWalkStart can return a WalkFlow.SKIP to stop the walk method execution of a particular "property schema".
234+
3. onWalkEnd will be called even if the onWalkStart returns a WalkFlow.SKIP.
235+
4. Walking a property will check if the keywords defined in the "property schema" has any keyword listeners, and they will be called in the defined order.
236+
For example in the above schema when we walk through the "name" property if there are any keyword listeners defined for "type" or "maxlength" , they will be invoked in the defined order.
237+
5. Since we have a property listener defined, When we are walking through a property that has a "$ref" keyword which might have some more properties defined,
238+
Our property listener would be invoked for each of the property defined in the "$ref" schema.
239+
6. As mentioned earlier anywhere during the "Walk Flow", we can return a WalkFlow.SKIP from onWalkStart method to stop the walk method of a particular "property schema" from being called.
240+
Since the walk method will not be called any property or keyword listeners in the "property schema" will not be invoked.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
import com.fasterxml.jackson.databind.JsonNode;
3333
import com.networknt.schema.walk.DefaultKeywordWalkListenerRunner;
34-
import com.networknt.schema.walk.JsonWalker;
34+
import com.networknt.schema.walk.JsonSchemaWalker;
3535
import com.networknt.schema.walk.WalkListenerRunner;
3636

3737
/**
@@ -288,7 +288,7 @@ public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at,
288288
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
289289
// Walk through all the JSONWalker's.
290290
for (Entry<String, JsonValidator> entry : validators.entrySet()) {
291-
JsonWalker jsonWalker = entry.getValue();
291+
JsonSchemaWalker jsonWalker = entry.getValue();
292292
String schemaPathWithKeyword = entry.getKey();
293293
try {
294294
// Call all the pre-walk listeners. If all the pre-walk listeners return true

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
import java.util.Set;
2020

2121
import com.fasterxml.jackson.databind.JsonNode;
22-
import com.networknt.schema.walk.JsonWalker;
22+
import com.networknt.schema.walk.JsonSchemaWalker;
2323

2424
/**
2525
* Standard json validator interface, implemented by all validators and JsonSchema.
2626
*/
27-
public interface JsonValidator extends JsonWalker {
27+
public interface JsonValidator extends JsonSchemaWalker {
2828
public static final String AT_ROOT = "$";
2929

3030

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

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import java.util.Set;
2424

2525
import com.fasterxml.jackson.databind.JsonNode;
26-
import com.networknt.schema.walk.WalkListener;
26+
import com.networknt.schema.walk.JsonSchemaWalkListener;
2727

2828
public class SchemaValidatorsConfig {
2929
/**
@@ -61,9 +61,9 @@ public class SchemaValidatorsConfig {
6161
// This is just a constant for listening to all Keywords.
6262
public static final String ALL_KEYWORD_WALK_LISTENER_KEY = "com.networknt.AllKeywordWalkListener";
6363

64-
private final Map<String, List<WalkListener>> keywordWalkListenersMap = new HashMap<String, List<WalkListener>>();
64+
private final Map<String, List<JsonSchemaWalkListener>> keywordWalkListenersMap = new HashMap<String, List<JsonSchemaWalkListener>>();
6565

66-
private final List<WalkListener> propertyWalkListeners = new ArrayList<WalkListener>();
66+
private final List<JsonSchemaWalkListener> propertyWalkListeners = new ArrayList<JsonSchemaWalkListener>();
6767

6868
public boolean isTypeLoose() {
6969
return typeLoose;
@@ -113,52 +113,52 @@ public void setEcma262Validator(boolean ecma262Validator) {
113113
this.ecma262Validator = ecma262Validator;
114114
}
115115

116-
public void addKeywordWalkListener(WalkListener keywordWalkListener) {
116+
public void addKeywordWalkListener(JsonSchemaWalkListener keywordWalkListener) {
117117
if (keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY) == null) {
118-
List<WalkListener> keywordWalkListeners = new ArrayList<WalkListener>();
118+
List<JsonSchemaWalkListener> keywordWalkListeners = new ArrayList<JsonSchemaWalkListener>();
119119
keywordWalkListenersMap.put(ALL_KEYWORD_WALK_LISTENER_KEY, keywordWalkListeners);
120120
}
121121
keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY).add(keywordWalkListener);
122122
}
123123

124-
public void addKeywordWalkListener(String keyword, WalkListener keywordWalkListener) {
124+
public void addKeywordWalkListener(String keyword, JsonSchemaWalkListener keywordWalkListener) {
125125
if (keywordWalkListenersMap.get(keyword) == null) {
126-
List<WalkListener> keywordWalkListeners = new ArrayList<WalkListener>();
126+
List<JsonSchemaWalkListener> keywordWalkListeners = new ArrayList<JsonSchemaWalkListener>();
127127
keywordWalkListenersMap.put(keyword, keywordWalkListeners);
128128
}
129129
keywordWalkListenersMap.get(keyword).add(keywordWalkListener);
130130
}
131131

132132

133-
public void addKeywordWalkListeners(List<WalkListener> keywordWalkListeners) {
133+
public void addKeywordWalkListeners(List<JsonSchemaWalkListener> keywordWalkListeners) {
134134
if (keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY) == null) {
135-
List<WalkListener> ikeywordWalkListeners = new ArrayList<WalkListener>();
135+
List<JsonSchemaWalkListener> ikeywordWalkListeners = new ArrayList<JsonSchemaWalkListener>();
136136
keywordWalkListenersMap.put(ALL_KEYWORD_WALK_LISTENER_KEY, ikeywordWalkListeners);
137137
}
138138
keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY).addAll(keywordWalkListeners);
139139
}
140140

141-
public void addKeywordWalkListeners(String keyword, List<WalkListener> keywordWalkListeners) {
141+
public void addKeywordWalkListeners(String keyword, List<JsonSchemaWalkListener> keywordWalkListeners) {
142142
if (keywordWalkListenersMap.get(keyword) == null) {
143-
List<WalkListener> ikeywordWalkListeners = new ArrayList<WalkListener>();
143+
List<JsonSchemaWalkListener> ikeywordWalkListeners = new ArrayList<JsonSchemaWalkListener>();
144144
keywordWalkListenersMap.put(keyword, ikeywordWalkListeners);
145145
}
146146
keywordWalkListenersMap.get(keyword).addAll(keywordWalkListeners);
147147
}
148148

149-
public void addPropertyWalkListeners(List<WalkListener> propertyWalkListeners) {
149+
public void addPropertyWalkListeners(List<JsonSchemaWalkListener> propertyWalkListeners) {
150150
this.propertyWalkListeners.addAll(propertyWalkListeners);
151151
}
152152

153-
public void addPropertyWalkListener(WalkListener propertyWalkListener) {
153+
public void addPropertyWalkListener(JsonSchemaWalkListener propertyWalkListener) {
154154
this.propertyWalkListeners.add(propertyWalkListener);
155155
}
156156

157-
public List<WalkListener> getPropertyWalkListeners() {
157+
public List<JsonSchemaWalkListener> getPropertyWalkListeners() {
158158
return this.propertyWalkListeners;
159159
}
160160

161-
public Map<String, List<WalkListener>> getKeywordWalkListenersMap() {
161+
public Map<String, List<JsonSchemaWalkListener>> getKeywordWalkListenersMap() {
162162
return this.keywordWalkListenersMap;
163163
}
164164

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ protected WalkEvent constructWalkEvent(String keyWordName, JsonNode node, JsonNo
2222
.currentJsonSchemaFactory(currentJsonSchemaFactory).build();
2323
}
2424

25-
protected boolean runPreWalkListeners(List<WalkListener> walkListeners, WalkEvent walkEvent) {
25+
protected boolean runPreWalkListeners(List<JsonSchemaWalkListener> walkListeners, WalkEvent walkEvent) {
2626
boolean continueRunningListenersAndWalk = true;
2727
if (walkListeners != null) {
28-
for (WalkListener walkListener : walkListeners) {
28+
for (JsonSchemaWalkListener walkListener : walkListeners) {
2929
if (WalkFlow.SKIP.equals(walkListener.onWalkStart(walkEvent))) {
3030
continueRunningListenersAndWalk = false;
3131
break;
@@ -35,10 +35,10 @@ protected boolean runPreWalkListeners(List<WalkListener> walkListeners, WalkEven
3535
return continueRunningListenersAndWalk;
3636
}
3737

38-
protected void runPostWalkListeners(List<WalkListener> walkListeners, WalkEvent walkEvent,
39-
Set<ValidationMessage> validationMessages) {
38+
protected void runPostWalkListeners(List<JsonSchemaWalkListener> walkListeners, WalkEvent walkEvent,
39+
Set<ValidationMessage> validationMessages) {
4040
if (walkListeners != null) {
41-
for (WalkListener walkListener : walkListeners) {
41+
for (JsonSchemaWalkListener walkListener : walkListeners) {
4242
walkListener.onWalkEnd(walkEvent, validationMessages);
4343
}
4444
}

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212

1313
public class DefaultKeywordWalkListenerRunner extends AbstractWalkListenerRunner {
1414

15-
private Map<String, List<WalkListener>> keywordWalkListenersMap;
15+
private Map<String, List<JsonSchemaWalkListener>> keywordWalkListenersMap;
1616

17-
public DefaultKeywordWalkListenerRunner(Map<String, List<WalkListener>> keywordWalkListenersMap) {
17+
public DefaultKeywordWalkListenerRunner(Map<String, List<JsonSchemaWalkListener>> keywordWalkListenersMap) {
1818
this.keywordWalkListenersMap = keywordWalkListenersMap;
1919
}
2020

@@ -27,11 +27,11 @@ public boolean runPreWalkListeners(String keyWordPath, JsonNode node, JsonNode r
2727
WalkEvent keywordWalkEvent = constructWalkEvent(keyword, node, rootNode, at, schemaPath, schemaNode,
2828
parentSchema, currentJsonSchemaFactory);
2929
// Run Listeners that are setup only for this keyword.
30-
List<WalkListener> currentKeywordListeners = keywordWalkListenersMap.get(keyword);
30+
List<JsonSchemaWalkListener> currentKeywordListeners = keywordWalkListenersMap.get(keyword);
3131
continueRunningListenersAndWalk = runPreWalkListeners(currentKeywordListeners, keywordWalkEvent);
3232
if (continueRunningListenersAndWalk) {
3333
// Run Listeners that are setup for all keywords.
34-
List<WalkListener> allKeywordListeners = keywordWalkListenersMap
34+
List<JsonSchemaWalkListener> allKeywordListeners = keywordWalkListenersMap
3535
.get(SchemaValidatorsConfig.ALL_KEYWORD_WALK_LISTENER_KEY);
3636
runPreWalkListeners(allKeywordListeners, keywordWalkEvent);
3737
}
@@ -46,10 +46,10 @@ public void runPostWalkListeners(String keyWordPath, JsonNode node, JsonNode roo
4646
WalkEvent keywordWalkEvent = constructWalkEvent(keyword, node, rootNode, at, schemaPath, schemaNode,
4747
parentSchema, currentJsonSchemaFactory);
4848
// Run Listeners that are setup only for this keyword.
49-
List<WalkListener> currentKeywordListeners = keywordWalkListenersMap.get(keyword);
49+
List<JsonSchemaWalkListener> currentKeywordListeners = keywordWalkListenersMap.get(keyword);
5050
runPostWalkListeners(currentKeywordListeners, keywordWalkEvent, validationMessages);
5151
// Run Listeners that are setup for all keywords.
52-
List<WalkListener> allKeywordListeners = keywordWalkListenersMap
52+
List<JsonSchemaWalkListener> allKeywordListeners = keywordWalkListenersMap
5353
.get(SchemaValidatorsConfig.ALL_KEYWORD_WALK_LISTENER_KEY);
5454
runPostWalkListeners(allKeywordListeners, keywordWalkEvent, validationMessages);
5555
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010

1111
public class DefaultPropertyWalkListenerRunner extends AbstractWalkListenerRunner {
1212

13-
private List<WalkListener> propertyWalkListeners;
13+
private List<JsonSchemaWalkListener> propertyWalkListeners;
1414

15-
public DefaultPropertyWalkListenerRunner(List<WalkListener> propertyWalkListeners) {
15+
public DefaultPropertyWalkListenerRunner(List<JsonSchemaWalkListener> propertyWalkListeners) {
1616
this.propertyWalkListeners = propertyWalkListeners;
1717
}
1818

src/main/java/com/networknt/schema/walk/WalkListener.java renamed to src/main/java/com/networknt/schema/walk/JsonSchemaWalkListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* Listener class that captures walkStart and walkEnd events.
1010
*
1111
*/
12-
public interface WalkListener {
12+
public interface JsonSchemaWalkListener {
1313

1414
public WalkFlow onWalkStart(WalkEvent walkEvent);
1515

0 commit comments

Comments
 (0)