xref: /aosp_15_r20/external/json-schema-validator/doc/walkers.md (revision 78c4dd6aa35290980cdcd1623a7e337e8d021c7c)
1*78c4dd6aSAndroid Build Coastguard Worker### JSON Schema Walkers
2*78c4dd6aSAndroid Build Coastguard Worker
3*78c4dd6aSAndroid Build Coastguard WorkerThere 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, or applying default values. JSON walkers were introduced to complement the validation functionality this library already provides.
4*78c4dd6aSAndroid Build Coastguard Worker
5*78c4dd6aSAndroid Build Coastguard WorkerCurrently, walking is defined at the validator instance level for all the built-in keywords.
6*78c4dd6aSAndroid Build Coastguard Worker
7*78c4dd6aSAndroid Build Coastguard Worker### Walk methods
8*78c4dd6aSAndroid Build Coastguard Worker
9*78c4dd6aSAndroid Build Coastguard WorkerA 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.
10*78c4dd6aSAndroid Build Coastguard Worker
11*78c4dd6aSAndroid Build Coastguard Worker```java
12*78c4dd6aSAndroid Build Coastguard Workerpublic interface JsonSchemaWalker {
13*78c4dd6aSAndroid Build Coastguard Worker    /**
14*78c4dd6aSAndroid Build Coastguard Worker     *
15*78c4dd6aSAndroid Build Coastguard Worker     * This method gives the capability to walk through the given JsonNode, allowing
16*78c4dd6aSAndroid Build Coastguard Worker     * functionality beyond validation like collecting information,handling cross
17*78c4dd6aSAndroid Build Coastguard Worker     * cutting concerns like logging or instrumentation. This method also performs
18*78c4dd6aSAndroid Build Coastguard Worker     * the validation if {@code shouldValidateSchema} is set to true. <br>
19*78c4dd6aSAndroid Build Coastguard Worker     * <br>
20*78c4dd6aSAndroid Build Coastguard Worker     * {@link BaseJsonValidator#walk(ExecutionContext, JsonNode, JsonNode, JsonNodePath, boolean)} provides
21*78c4dd6aSAndroid Build Coastguard Worker     * a default implementation of this method. However validators that parse
22*78c4dd6aSAndroid Build Coastguard Worker     * sub-schemas should override this method to call walk method on those
23*78c4dd6aSAndroid Build Coastguard Worker     * sub-schemas.
24*78c4dd6aSAndroid Build Coastguard Worker     *
25*78c4dd6aSAndroid Build Coastguard Worker     * @param executionContext     ExecutionContext
26*78c4dd6aSAndroid Build Coastguard Worker     * @param node                 JsonNode
27*78c4dd6aSAndroid Build Coastguard Worker     * @param rootNode             JsonNode
28*78c4dd6aSAndroid Build Coastguard Worker     * @param instanceLocation     JsonNodePath
29*78c4dd6aSAndroid Build Coastguard Worker     * @param shouldValidateSchema boolean
30*78c4dd6aSAndroid Build Coastguard Worker     * @return a set of validation messages if shouldValidateSchema is true.
31*78c4dd6aSAndroid Build Coastguard Worker     */
32*78c4dd6aSAndroid Build Coastguard Worker    Set<ValidationMessage> walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode,
33*78c4dd6aSAndroid Build Coastguard Worker            JsonNodePath instanceLocation, boolean shouldValidateSchema);
34*78c4dd6aSAndroid Build Coastguard Worker}
35*78c4dd6aSAndroid Build Coastguard Worker
36*78c4dd6aSAndroid Build Coastguard Worker```
37*78c4dd6aSAndroid Build Coastguard Worker
38*78c4dd6aSAndroid Build Coastguard WorkerThe 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.
39*78c4dd6aSAndroid Build Coastguard Worker
40*78c4dd6aSAndroid Build Coastguard Worker```java
41*78c4dd6aSAndroid Build Coastguard Worker    /**
42*78c4dd6aSAndroid Build Coastguard Worker     * This is default implementation of walk method. Its job is to call the
43*78c4dd6aSAndroid Build Coastguard Worker     * validate method if shouldValidateSchema is enabled.
44*78c4dd6aSAndroid Build Coastguard Worker     */
45*78c4dd6aSAndroid Build Coastguard Worker    @Override
46*78c4dd6aSAndroid Build Coastguard Worker    default Set<ValidationMessage> walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode,
47*78c4dd6aSAndroid Build Coastguard Worker            JsonNodePath instanceLocation, boolean shouldValidateSchema) {
48*78c4dd6aSAndroid Build Coastguard Worker        return shouldValidateSchema ? validate(executionContext, node, rootNode, instanceLocation)
49*78c4dd6aSAndroid Build Coastguard Worker                : Collections.emptySet();
50*78c4dd6aSAndroid Build Coastguard Worker    }
51*78c4dd6aSAndroid Build Coastguard Worker```
52*78c4dd6aSAndroid Build Coastguard Worker
53*78c4dd6aSAndroid Build Coastguard WorkerA new walk method added to the JSONSchema class allows us to walk through the JSONSchema.
54*78c4dd6aSAndroid Build Coastguard Worker
55*78c4dd6aSAndroid Build Coastguard Worker```java
56*78c4dd6aSAndroid Build Coastguard Worker    public ValidationResult walk(JsonNode node, boolean validate) {
57*78c4dd6aSAndroid Build Coastguard Worker        return walk(createExecutionContext(), node, validate);
58*78c4dd6aSAndroid Build Coastguard Worker    }
59*78c4dd6aSAndroid Build Coastguard Worker
60*78c4dd6aSAndroid Build Coastguard Worker    @Override
61*78c4dd6aSAndroid Build Coastguard Worker    public Set<ValidationMessage> walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode,
62*78c4dd6aSAndroid Build Coastguard Worker            JsonNodePath instanceLocation, boolean shouldValidateSchema) {
63*78c4dd6aSAndroid Build Coastguard Worker        Set<ValidationMessage> errors = new LinkedHashSet<>();
64*78c4dd6aSAndroid Build Coastguard Worker        // Walk through all the JSONWalker's.
65*78c4dd6aSAndroid Build Coastguard Worker        for (JsonValidator validator : getValidators()) {
66*78c4dd6aSAndroid Build Coastguard Worker            JsonNodePath evaluationPathWithKeyword = validator.getEvaluationPath();
67*78c4dd6aSAndroid Build Coastguard Worker            try {
68*78c4dd6aSAndroid Build Coastguard Worker                // Call all the pre-walk listeners. If at least one of the pre walk listeners
69*78c4dd6aSAndroid Build Coastguard Worker                // returns SKIP, then skip the walk.
70*78c4dd6aSAndroid Build Coastguard Worker                if (this.validationContext.getConfig().getKeywordWalkListenerRunner().runPreWalkListeners(executionContext,
71*78c4dd6aSAndroid Build Coastguard Worker                        evaluationPathWithKeyword.getName(-1), node, rootNode, instanceLocation,
72*78c4dd6aSAndroid Build Coastguard Worker                        this, validator)) {
73*78c4dd6aSAndroid Build Coastguard Worker                    Set<ValidationMessage> results = null;
74*78c4dd6aSAndroid Build Coastguard Worker                    try {
75*78c4dd6aSAndroid Build Coastguard Worker                        results = validator.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
76*78c4dd6aSAndroid Build Coastguard Worker                    } finally {
77*78c4dd6aSAndroid Build Coastguard Worker                        if (results != null && !results.isEmpty()) {
78*78c4dd6aSAndroid Build Coastguard Worker                            errors.addAll(results);
79*78c4dd6aSAndroid Build Coastguard Worker                        }
80*78c4dd6aSAndroid Build Coastguard Worker                    }
81*78c4dd6aSAndroid Build Coastguard Worker                }
82*78c4dd6aSAndroid Build Coastguard Worker            } finally {
83*78c4dd6aSAndroid Build Coastguard Worker                // Call all the post-walk listeners.
84*78c4dd6aSAndroid Build Coastguard Worker                this.validationContext.getConfig().getKeywordWalkListenerRunner().runPostWalkListeners(executionContext,
85*78c4dd6aSAndroid Build Coastguard Worker                        evaluationPathWithKeyword.getName(-1), node, rootNode, instanceLocation,
86*78c4dd6aSAndroid Build Coastguard Worker                        this, validator, errors);
87*78c4dd6aSAndroid Build Coastguard Worker            }
88*78c4dd6aSAndroid Build Coastguard Worker        }
89*78c4dd6aSAndroid Build Coastguard Worker        return errors;
90*78c4dd6aSAndroid Build Coastguard Worker    }
91*78c4dd6aSAndroid Build Coastguard Worker```
92*78c4dd6aSAndroid Build Coastguard WorkerFollowing code snippet shows how to call the walk method on a JsonSchema instance.
93*78c4dd6aSAndroid Build Coastguard Worker
94*78c4dd6aSAndroid Build Coastguard Worker```java
95*78c4dd6aSAndroid Build Coastguard WorkerValidationResult result = jsonSchema.walk(data, false);
96*78c4dd6aSAndroid Build Coastguard Worker
97*78c4dd6aSAndroid Build Coastguard Worker```
98*78c4dd6aSAndroid Build Coastguard Worker
99*78c4dd6aSAndroid Build Coastguard Workerwalk 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*78c4dd6aSAndroid Build Coastguard Worker
101*78c4dd6aSAndroid Build Coastguard Worker### Walk Listeners
102*78c4dd6aSAndroid Build Coastguard Worker
103*78c4dd6aSAndroid Build Coastguard WorkerWalk 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.
104*78c4dd6aSAndroid Build Coastguard Worker
105*78c4dd6aSAndroid Build Coastguard Worker```java
106*78c4dd6aSAndroid Build Coastguard Workerpublic interface JsonSchemaWalkListener {
107*78c4dd6aSAndroid Build Coastguard Worker
108*78c4dd6aSAndroid Build Coastguard Worker	public WalkFlow onWalkStart(WalkEvent walkEvent);
109*78c4dd6aSAndroid Build Coastguard Worker
110*78c4dd6aSAndroid Build Coastguard Worker	public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages);
111*78c4dd6aSAndroid Build Coastguard Worker}
112*78c4dd6aSAndroid Build Coastguard Worker```
113*78c4dd6aSAndroid Build Coastguard Worker
114*78c4dd6aSAndroid Build Coastguard WorkerFollowing is the example of a sample WalkListener implementation.
115*78c4dd6aSAndroid Build Coastguard Worker
116*78c4dd6aSAndroid Build Coastguard Worker```java
117*78c4dd6aSAndroid Build Coastguard Workerprivate static class PropertiesKeywordListener implements JsonSchemaWalkListener {
118*78c4dd6aSAndroid Build Coastguard Worker
119*78c4dd6aSAndroid Build Coastguard Worker        @Override
120*78c4dd6aSAndroid Build Coastguard Worker        public WalkFlow onWalkStart(WalkEvent keywordWalkEvent) {
121*78c4dd6aSAndroid Build Coastguard Worker            JsonNode schemaNode = keywordWalkEvent.getSchema().getSchemaNode();
122*78c4dd6aSAndroid Build Coastguard Worker            if (schemaNode.get("title").textValue().equals("Property3")) {
123*78c4dd6aSAndroid Build Coastguard Worker                return WalkFlow.SKIP;
124*78c4dd6aSAndroid Build Coastguard Worker            }
125*78c4dd6aSAndroid Build Coastguard Worker            return WalkFlow.CONTINUE;
126*78c4dd6aSAndroid Build Coastguard Worker        }
127*78c4dd6aSAndroid Build Coastguard Worker
128*78c4dd6aSAndroid Build Coastguard Worker        @Override
129*78c4dd6aSAndroid Build Coastguard Worker        public void onWalkEnd(WalkEvent keywordWalkEvent, Set<ValidationMessage> validationMessages) {
130*78c4dd6aSAndroid Build Coastguard Worker
131*78c4dd6aSAndroid Build Coastguard Worker        }
132*78c4dd6aSAndroid Build Coastguard Worker    }
133*78c4dd6aSAndroid Build Coastguard Worker```
134*78c4dd6aSAndroid Build Coastguard WorkerIf the onWalkStart method returns WalkFlow.SKIP, the actual walk method execution will be skipped.
135*78c4dd6aSAndroid Build Coastguard Worker
136*78c4dd6aSAndroid Build Coastguard WorkerWalk listeners can be added by using the SchemaValidatorsConfig class.
137*78c4dd6aSAndroid Build Coastguard Worker
138*78c4dd6aSAndroid Build Coastguard Worker```java
139*78c4dd6aSAndroid Build Coastguard WorkerSchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig();
140*78c4dd6aSAndroid Build Coastguard Worker    schemaValidatorsConfig.addKeywordWalkListener(new AllKeywordListener());
141*78c4dd6aSAndroid Build Coastguard Worker    schemaValidatorsConfig.addKeywordWalkListener(ValidatorTypeCode.REF.getValue(), new RefKeywordListener());
142*78c4dd6aSAndroid Build Coastguard Worker    schemaValidatorsConfig.addKeywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(),
143*78c4dd6aSAndroid Build Coastguard Worker            new PropertiesKeywordListener());
144*78c4dd6aSAndroid Build Coastguard Workerfinal JsonSchemaFactory schemaFactory = JsonSchemaFactory
145*78c4dd6aSAndroid Build Coastguard Worker        .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).metaSchema(metaSchema)
146*78c4dd6aSAndroid Build Coastguard Worker        .build();
147*78c4dd6aSAndroid Build Coastguard Workerthis.jsonSchema = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig);
148*78c4dd6aSAndroid Build Coastguard Worker
149*78c4dd6aSAndroid Build Coastguard Worker```
150*78c4dd6aSAndroid Build Coastguard Worker
151*78c4dd6aSAndroid Build Coastguard WorkerThere 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.
152*78c4dd6aSAndroid Build Coastguard Worker
153*78c4dd6aSAndroid Build Coastguard WorkerBoth 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.
154*78c4dd6aSAndroid Build Coastguard Worker
155*78c4dd6aSAndroid Build Coastguard Worker```java
156*78c4dd6aSAndroid Build Coastguard WorkerSchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig();
157*78c4dd6aSAndroid Build Coastguard WorkerschemaValidatorsConfig.addPropertyWalkListener(new ExamplePropertyWalkListener());
158*78c4dd6aSAndroid Build Coastguard Workerfinal JsonSchemaFactory schemaFactory = JsonSchemaFactory
159*78c4dd6aSAndroid Build Coastguard Worker                .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).metaSchema(metaSchema)
160*78c4dd6aSAndroid Build Coastguard Worker                .build();
161*78c4dd6aSAndroid Build Coastguard Workerthis.jsonSchema = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig);
162*78c4dd6aSAndroid Build Coastguard Worker
163*78c4dd6aSAndroid Build Coastguard Worker```
164*78c4dd6aSAndroid Build Coastguard Worker
165*78c4dd6aSAndroid Build Coastguard Worker### Walk Events
166*78c4dd6aSAndroid Build Coastguard Worker
167*78c4dd6aSAndroid Build Coastguard WorkerAn instance of WalkEvent is passed to both the onWalkStart and onWalkEnd methods of the WalkListeners implementations.
168*78c4dd6aSAndroid Build Coastguard Worker
169*78c4dd6aSAndroid Build Coastguard WorkerA WalkEvent instance captures several details about the node currently being walked along with the schema of the node, Json path of the node and other details.
170*78c4dd6aSAndroid Build Coastguard Worker
171*78c4dd6aSAndroid Build Coastguard WorkerFollowing snippet shows the details captured by WalkEvent instance.
172*78c4dd6aSAndroid Build Coastguard Worker
173*78c4dd6aSAndroid Build Coastguard Worker```java
174*78c4dd6aSAndroid Build Coastguard Workerpublic class WalkEvent {
175*78c4dd6aSAndroid Build Coastguard Worker    private ExecutionContext executionContext;
176*78c4dd6aSAndroid Build Coastguard Worker    private JsonSchema schema;
177*78c4dd6aSAndroid Build Coastguard Worker    private String keyword;
178*78c4dd6aSAndroid Build Coastguard Worker    private JsonNode rootNode;
179*78c4dd6aSAndroid Build Coastguard Worker    private JsonNode instanceNode;
180*78c4dd6aSAndroid Build Coastguard Worker    private JsonNodePath instanceLocation;
181*78c4dd6aSAndroid Build Coastguard Worker    private JsonValidator validator;
182*78c4dd6aSAndroid Build Coastguard Worker    ...
183*78c4dd6aSAndroid Build Coastguard Worker}
184*78c4dd6aSAndroid Build Coastguard Worker```
185*78c4dd6aSAndroid Build Coastguard Worker
186*78c4dd6aSAndroid Build Coastguard Worker### Sample Flow
187*78c4dd6aSAndroid Build Coastguard Worker
188*78c4dd6aSAndroid Build Coastguard WorkerGiven an example schema as shown, if we write a property listener, the walk flow is as depicted in the image.
189*78c4dd6aSAndroid Build Coastguard Worker
190*78c4dd6aSAndroid Build Coastguard Worker```json
191*78c4dd6aSAndroid Build Coastguard Worker{
192*78c4dd6aSAndroid Build Coastguard Worker
193*78c4dd6aSAndroid Build Coastguard Worker    "title": "Sample Schema",
194*78c4dd6aSAndroid Build Coastguard Worker    "definitions" : {
195*78c4dd6aSAndroid Build Coastguard Worker      "address" :{
196*78c4dd6aSAndroid Build Coastguard Worker       "street-address": {
197*78c4dd6aSAndroid Build Coastguard Worker            "title": "Street Address",
198*78c4dd6aSAndroid Build Coastguard Worker            "type": "string"
199*78c4dd6aSAndroid Build Coastguard Worker        },
200*78c4dd6aSAndroid Build Coastguard Worker        "pincode": {
201*78c4dd6aSAndroid Build Coastguard Worker            "title": "Body",
202*78c4dd6aSAndroid Build Coastguard Worker            "type": "integer"
203*78c4dd6aSAndroid Build Coastguard Worker        }
204*78c4dd6aSAndroid Build Coastguard Worker      }
205*78c4dd6aSAndroid Build Coastguard Worker    },
206*78c4dd6aSAndroid Build Coastguard Worker    "properties": {
207*78c4dd6aSAndroid Build Coastguard Worker        "name": {
208*78c4dd6aSAndroid Build Coastguard Worker            "title": "Title",
209*78c4dd6aSAndroid Build Coastguard Worker            "type": "string",
210*78c4dd6aSAndroid Build Coastguard Worker            "maxLength": 50
211*78c4dd6aSAndroid Build Coastguard Worker        },
212*78c4dd6aSAndroid Build Coastguard Worker        "body": {
213*78c4dd6aSAndroid Build Coastguard Worker            "title": "Body",
214*78c4dd6aSAndroid Build Coastguard Worker            "type": "string"
215*78c4dd6aSAndroid Build Coastguard Worker        },
216*78c4dd6aSAndroid Build Coastguard Worker        "address": {
217*78c4dd6aSAndroid Build Coastguard Worker            "title": "Excerpt",
218*78c4dd6aSAndroid Build Coastguard Worker            "$ref": "#/definitions/address"
219*78c4dd6aSAndroid Build Coastguard Worker        }
220*78c4dd6aSAndroid Build Coastguard Worker
221*78c4dd6aSAndroid Build Coastguard Worker    },
222*78c4dd6aSAndroid Build Coastguard Worker    "additionalProperties": false
223*78c4dd6aSAndroid Build Coastguard Worker}
224*78c4dd6aSAndroid Build Coastguard Worker```
225*78c4dd6aSAndroid Build Coastguard Worker
226*78c4dd6aSAndroid Build Coastguard Worker![img](walk_flow.png)<!-- .element height="50%" width="50%" -->
227*78c4dd6aSAndroid Build Coastguard Worker
228*78c4dd6aSAndroid Build Coastguard Worker
229*78c4dd6aSAndroid Build Coastguard WorkerFew important points to note about the flow.
230*78c4dd6aSAndroid Build Coastguard Worker
231*78c4dd6aSAndroid Build Coastguard Worker1. onWalkStart and onWalkEnd are the methods defined in the property walk listener
232*78c4dd6aSAndroid Build Coastguard Worker2. Anywhere during the flow, onWalkStart can return a WalkFlow.SKIP to stop the walk method execution of a particular "property schema".
233*78c4dd6aSAndroid Build Coastguard Worker3. onWalkEnd will be called even if the onWalkStart returns a WalkFlow.SKIP.
234*78c4dd6aSAndroid Build Coastguard Worker4. 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.
235*78c4dd6aSAndroid Build Coastguard Worker   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.
236*78c4dd6aSAndroid Build Coastguard Worker5. 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,
237*78c4dd6aSAndroid Build Coastguard Worker   Our property listener would be invoked for each of the property defined in the "$ref" schema.
238*78c4dd6aSAndroid Build Coastguard Worker6. 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.
239*78c4dd6aSAndroid Build Coastguard Worker   Since the walk method will not be called any property or keyword listeners in the "property schema" will not be invoked.
240*78c4dd6aSAndroid Build Coastguard Worker
241*78c4dd6aSAndroid Build Coastguard Worker
242*78c4dd6aSAndroid Build Coastguard Worker### Applying defaults
243*78c4dd6aSAndroid Build Coastguard Worker
244*78c4dd6aSAndroid Build Coastguard WorkerIn some use cases we may want to apply defaults while walking the schema.
245*78c4dd6aSAndroid Build Coastguard WorkerTo accomplish this, create an ApplyDefaultsStrategy when creating a SchemaValidatorsConfig.
246*78c4dd6aSAndroid Build Coastguard WorkerThe input object is changed in place, even if validation fails, or a fail-fast or some other exception is thrown.
247*78c4dd6aSAndroid Build Coastguard Worker
248*78c4dd6aSAndroid Build Coastguard WorkerHere is the order of operations in walker.
249*78c4dd6aSAndroid Build Coastguard Worker1. apply defaults
250*78c4dd6aSAndroid Build Coastguard Worker1. run listeners
251*78c4dd6aSAndroid Build Coastguard Worker1. validate if shouldValidateSchema is true
252*78c4dd6aSAndroid Build Coastguard Worker
253*78c4dd6aSAndroid Build Coastguard WorkerSuppose the JSON schema is
254*78c4dd6aSAndroid Build Coastguard Worker```json
255*78c4dd6aSAndroid Build Coastguard Worker{
256*78c4dd6aSAndroid Build Coastguard Worker  "$schema": "http://json-schema.org/draft-04/schema#",
257*78c4dd6aSAndroid Build Coastguard Worker  "title": "Schema with default values ",
258*78c4dd6aSAndroid Build Coastguard Worker  "type": "object",
259*78c4dd6aSAndroid Build Coastguard Worker  "properties": {
260*78c4dd6aSAndroid Build Coastguard Worker    "intValue": {
261*78c4dd6aSAndroid Build Coastguard Worker      "type": "integer",
262*78c4dd6aSAndroid Build Coastguard Worker      "default": 15,
263*78c4dd6aSAndroid Build Coastguard Worker      "minimum": 20
264*78c4dd6aSAndroid Build Coastguard Worker    }
265*78c4dd6aSAndroid Build Coastguard Worker  },
266*78c4dd6aSAndroid Build Coastguard Worker  "required": ["intValue"]
267*78c4dd6aSAndroid Build Coastguard Worker}
268*78c4dd6aSAndroid Build Coastguard Worker```
269*78c4dd6aSAndroid Build Coastguard Worker
270*78c4dd6aSAndroid Build Coastguard WorkerA JSON file like
271*78c4dd6aSAndroid Build Coastguard Worker```json
272*78c4dd6aSAndroid Build Coastguard Worker{
273*78c4dd6aSAndroid Build Coastguard Worker}
274*78c4dd6aSAndroid Build Coastguard Worker```
275*78c4dd6aSAndroid Build Coastguard Worker
276*78c4dd6aSAndroid Build Coastguard Workerwould normally fail validation as "intValue" is required.
277*78c4dd6aSAndroid Build Coastguard WorkerBut if we apply defaults while walking, then required validation passes, and the object is changed in place.
278*78c4dd6aSAndroid Build Coastguard Worker
279*78c4dd6aSAndroid Build Coastguard Worker```java
280*78c4dd6aSAndroid Build Coastguard Worker        JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
281*78c4dd6aSAndroid Build Coastguard Worker        SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig();
282*78c4dd6aSAndroid Build Coastguard Worker        schemaValidatorsConfig.setApplyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true));
283*78c4dd6aSAndroid Build Coastguard Worker        JsonSchema jsonSchema =  schemaFactory.getSchema(SchemaLocation.of("classpath:schema.json"), schemaValidatorsConfig);
284*78c4dd6aSAndroid Build Coastguard Worker
285*78c4dd6aSAndroid Build Coastguard Worker        JsonNode inputNode = objectMapper.readTree(getClass().getClassLoader().getResourceAsStream("data.json"));
286*78c4dd6aSAndroid Build Coastguard Worker        ValidationResult result = jsonSchema.walk(inputNode, true);
287*78c4dd6aSAndroid Build Coastguard Worker        assertThat(result.getValidationMessages(), Matchers.empty());
288*78c4dd6aSAndroid Build Coastguard Worker        assertEquals("{\"intValue\":15}", inputNode.toString());
289*78c4dd6aSAndroid Build Coastguard Worker        assertThat(result.getValidationMessages().stream().map(ValidationMessage::getMessage).collect(Collectors.toList()),
290*78c4dd6aSAndroid Build Coastguard Worker                   Matchers.containsInAnyOrder("$.intValue: must have a minimum value of 20."));
291*78c4dd6aSAndroid Build Coastguard Worker```
292