Skip to content

Commit 4d51a58

Browse files
aznan2Matti Hanssonmathan
authored
Stops unevaluatedProperties and unevaluatedItems being applied recursively (#827)
* Add support for subschema references in getSchema(URI) (#619) * Fixes unevaluatedProperties and unevaluatedItems being applied recursively (#826) --------- Co-authored-by: Matti Hansson <[email protected]> Co-authored-by: mathan <[email protected]>
1 parent b1572db commit 4d51a58

File tree

5 files changed

+121
-64
lines changed

5 files changed

+121
-64
lines changed

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

+11-25
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,11 @@
1616

1717
package com.networknt.schema;
1818

19-
import java.util.ArrayList;
20-
import java.util.Collections;
21-
import java.util.HashSet;
22-
import java.util.List;
23-
import java.util.Set;
24-
import java.util.regex.Pattern;
25-
import java.util.stream.Collectors;
26-
19+
import com.fasterxml.jackson.databind.JsonNode;
2720
import org.slf4j.Logger;
2821
import org.slf4j.LoggerFactory;
2922

30-
import com.fasterxml.jackson.databind.JsonNode;
31-
import com.networknt.schema.utils.JsonNodeUtil;
23+
import java.util.*;
3224

3325
public class UnevaluatedItemsValidator extends BaseJsonValidator {
3426
private static final Logger logger = LoggerFactory.getLogger(UnevaluatedItemsValidator.class);
@@ -49,7 +41,7 @@ public UnevaluatedItemsValidator(String schemaPath, JsonNode schemaNode, JsonSch
4941

5042
@Override
5143
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
52-
if (this.disabled) return Collections.emptySet();
44+
if (this.disabled || !node.isArray()) return Collections.emptySet();
5345

5446
debug(logger, node, rootNode, at);
5547
CollectorContext collectorContext = CollectorContext.getInstance();
@@ -92,21 +84,15 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
9284
}
9385
}
9486

95-
private static final Pattern NUMERIC = Pattern.compile("^\\d+$");
96-
9787
private Set<String> allPaths(JsonNode node, String at) {
98-
return JsonNodeUtil.allPaths(getPathType(), at, node)
99-
.stream()
100-
.filter(this::isArray)
101-
.collect(Collectors.toSet());
102-
}
103-
104-
private boolean isArray(String path) {
105-
String jsonPointer = getPathType().convertToJsonPointer(path);
106-
String[] segment = jsonPointer.split("/");
107-
if (0 == segment.length) return false;
108-
String lastSegment = segment[segment.length - 1];
109-
return NUMERIC.matcher(lastSegment).matches();
88+
PathType pathType = getPathType();
89+
Set<String> collector = new HashSet<>();
90+
int size = node.size();
91+
for (int i = 0; i < size; ++i) {
92+
String path = pathType.append(at, i);
93+
collector.add(path);
94+
}
95+
return collector;
11096
}
11197

11298
private Set<ValidationMessage> reportUnevaluatedPaths(Set<String> unevaluatedPaths) {

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

+8-19
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,10 @@
1717
package com.networknt.schema;
1818

1919
import com.fasterxml.jackson.databind.JsonNode;
20-
import com.networknt.schema.utils.JsonNodeUtil;
21-
2220
import org.slf4j.Logger;
2321
import org.slf4j.LoggerFactory;
2422

2523
import java.util.*;
26-
import java.util.regex.Pattern;
27-
import java.util.stream.Collectors;
2824

2925
public class UnevaluatedPropertiesValidator extends BaseJsonValidator {
3026
private static final Logger logger = LoggerFactory.getLogger(UnevaluatedPropertiesValidator.class);
@@ -45,7 +41,7 @@ public UnevaluatedPropertiesValidator(String schemaPath, JsonNode schemaNode, Js
4541

4642
@Override
4743
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
48-
if (this.disabled) return Collections.emptySet();
44+
if (this.disabled || !node.isObject()) return Collections.emptySet();
4945

5046
debug(logger, node, rootNode, at);
5147
CollectorContext collectorContext = CollectorContext.getInstance();
@@ -88,21 +84,14 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
8884
}
8985
}
9086

91-
private static final Pattern NUMERIC = Pattern.compile("^\\d+$");
92-
9387
private Set<String> allPaths(JsonNode node, String at) {
94-
return JsonNodeUtil.allPaths(getPathType(), at, node)
95-
.stream()
96-
.filter(this::isProperty)
97-
.collect(Collectors.toSet());
98-
}
99-
100-
private boolean isProperty(String path) {
101-
String jsonPointer = getPathType().convertToJsonPointer(path);
102-
String[] segment = jsonPointer.split("/");
103-
if (0 == segment.length) return false;
104-
String lastSegment = segment[segment.length - 1];
105-
return !NUMERIC.matcher(lastSegment).matches();
88+
PathType pathType = getPathType();
89+
Set<String> collector = new HashSet<>();
90+
node.fields().forEachRemaining(entry -> {
91+
String path = pathType.append(at, entry.getKey());
92+
collector.add(path);
93+
});
94+
return collector;
10695
}
10796

10897
private Set<ValidationMessage> reportUnevaluatedPaths(Set<String> unevaluatedPaths) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.networknt.schema;
2+
3+
import com.networknt.schema.SpecVersion.VersionFlag;
4+
import org.junit.jupiter.api.DisplayName;
5+
import org.junit.jupiter.api.DynamicNode;
6+
import org.junit.jupiter.api.TestFactory;
7+
8+
import java.util.stream.Stream;
9+
10+
@DisplayName("Unevaluated Items")
11+
public class UnevaluatedItemsTest extends AbstractJsonSchemaTestSuite {
12+
13+
@TestFactory
14+
@DisplayName("Draft 2019-09")
15+
Stream<DynamicNode> draft201909() {
16+
return createTests(VersionFlag.V201909, "src/test/resources/schema/unevaluatedTests/unevaluated-items-tests.json");
17+
}
18+
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[
2+
{
3+
"description": "unevaluatedItems should not affect sub-schemas",
4+
"schema": {
5+
"unevaluatedItems": {
6+
"type": "object"
7+
}
8+
},
9+
"tests": [
10+
{
11+
"description": "unevaluated item bar is in sub-schema",
12+
"data": [
13+
{
14+
"foo": ["bar"]
15+
}
16+
],
17+
"valid": true
18+
}
19+
]
20+
}
21+
]

src/test/resources/schema/unevaluatedTests/unevaluated-tests.json

+62-20
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,18 @@
2727
"unevaluatedProperties": false
2828
},
2929
"residence": {
30-
"flatNumber": {
31-
"type": "string"
32-
},
33-
"flatName": {
34-
"type": "string"
30+
"properties": {
31+
"flatNumber": {
32+
"type": "string"
33+
},
34+
"flatName": {
35+
"type": "string"
36+
},
37+
"landmark": {
38+
"type": "string"
39+
}
3540
},
36-
"landmark": {
37-
"type": "string"
38-
}
41+
"unevaluatedProperties": false
3942
}
4043
},
4144
"properties": {
@@ -202,10 +205,11 @@
202205
}
203206
}
204207
}
205-
]
206-
}
207-
},
208-
"unevaluatedProperties": false
208+
],
209+
"unevaluatedProperties": false
210+
},
211+
"unevaluatedProperties": false
212+
}
209213
},
210214
"tests": [
211215
{
@@ -220,6 +224,22 @@
220224
},
221225
"valid": true
222226
},
227+
{
228+
"description": "Data which satisfies 1 oneOf schemas, but fails due to unevaluated prop",
229+
"data": {
230+
"firstName": "First Name",
231+
"age": 18,
232+
"lastName": "Last Name",
233+
"vehicle": {
234+
"pontoons": "pontoons",
235+
"wheels": "wheels"
236+
}
237+
},
238+
"valid": false,
239+
"validationMessages": [
240+
"There are unevaluated properties at the following paths $.vehicle.wheels"
241+
]
242+
},
223243
{
224244
"description": "Data which satisfies 2 oneOf schemas",
225245
"data": {
@@ -328,10 +348,11 @@
328348
}
329349
}
330350
}
331-
]
332-
}
333-
},
334-
"unevaluatedProperties": false
351+
],
352+
"unevaluatedProperties": false
353+
},
354+
"unevaluatedProperties": false
355+
}
335356
},
336357
"tests": [
337358
{
@@ -453,10 +474,11 @@
453474
}
454475
}
455476
}
456-
]
457-
}
458-
},
459-
"unevaluatedProperties": false
477+
],
478+
"unevaluatedProperties": false
479+
},
480+
"unevaluatedProperties": false
481+
}
460482
},
461483
"tests": [
462484
{
@@ -634,6 +656,26 @@
634656
}
635657
]
636658
},
659+
{
660+
"description": "unevaluatedProperties should not affect sub-schemas",
661+
"schema": {
662+
"properties": {
663+
"foo": {}
664+
},
665+
"unevaluatedProperties": false
666+
},
667+
"tests": [
668+
{
669+
"description": "unevaluated property bar is in sub-schema",
670+
"data": {
671+
"foo": {
672+
"bar": "baz"
673+
}
674+
},
675+
"valid": true
676+
}
677+
]
678+
},
637679
{
638680
"description": "unevaluatedProperties true",
639681
"schema": {

0 commit comments

Comments
 (0)