Skip to content

Commit 76a4d9a

Browse files
committed
fixes #456 OneOf only validate the first sub schema
1 parent 455cf0a commit 76a4d9a

File tree

5 files changed

+116
-36
lines changed

5 files changed

+116
-36
lines changed

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

+10-36
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,26 @@
1717
package com.networknt.schema;
1818

1919
import com.fasterxml.jackson.databind.JsonNode;
20-
import com.fasterxml.jackson.databind.node.ArrayNode;
21-
import com.fasterxml.jackson.databind.node.NullNode;
22-
import com.fasterxml.jackson.databind.node.ObjectNode;
23-
import com.networknt.schema.utils.JsonNodeUtil;
2420
import org.slf4j.Logger;
2521
import org.slf4j.LoggerFactory;
2622

2723
import java.util.*;
2824

2925
public class OneOfValidator extends BaseJsonValidator implements JsonValidator {
30-
3126
private static final Logger logger = LoggerFactory.getLogger(OneOfValidator.class);
3227

3328
private final List<ShortcutValidator> schemas = new ArrayList<ShortcutValidator>();
3429

35-
private final ValidationContext validationContext;
36-
3730
private static class ShortcutValidator {
3831
private final JsonSchema schema;
3932
private final Map<String, String> constants;
40-
private final ValidationContext validationContext;
4133

4234
ShortcutValidator(JsonNode schemaNode, JsonSchema parentSchema,
4335
ValidationContext validationContext, JsonSchema schema) {
4436
JsonNode refNode = schemaNode.get(ValidatorTypeCode.REF.getValue());
4537
JsonSchema resolvedRefSchema = refNode != null && refNode.isTextual() ? RefValidator.getRefSchema(parentSchema, validationContext, refNode.textValue()).getSchema() : null;
4638
this.constants = extractConstants(schemaNode, resolvedRefSchema);
4739
this.schema = schema;
48-
this.validationContext = validationContext;
4940
}
5041

5142
private Map<String, String> extractConstants(JsonNode schemaNode, JsonSchema resolvedRefSchema) {
@@ -130,7 +121,6 @@ public OneOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
130121
JsonSchema childSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), childNode, parentSchema);
131122
schemas.add(new ShortcutValidator(childNode, parentSchema, validationContext, childSchema));
132123
}
133-
this.validationContext = validationContext;
134124
parseErrorCode(getValidatorType().getErrorCodeKey());
135125
}
136126

@@ -163,49 +153,33 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
163153
continue;
164154
}*/
165155

166-
//Check to see if it is already validated.
167-
if(!childErrors.isEmpty() && JsonNodeUtil.matchOneOfTypeNode(schemaNode,TypeFactory.getValueNodeType(node, this.validationContext.getConfig()))){
168-
continue;
169-
}
170-
171156
// get the current validator
172157
JsonSchema schema = validator.schema;
173-
174-
//Skip the validation when the current node is oneOf type and it is not equal to schemaType.
175-
if (JsonNodeUtil.matchOneOfTypeNode(schemaNode, TypeFactory.getValueNodeType(node, this.validationContext.getConfig())) &&
176-
!JsonNodeUtil.equalsToSchemaType(node, schema, this.validationContext.getConfig()) && !(JsonType.UNKNOWN.equals(JsonNodeUtil.getSchemaJsonType(schema)))) {
177-
continue;
178-
}
179-
180158
if (!state.isWalkEnabled()) {
181159
schemaErrors = schema.validate(node, rootNode, at);
182160
} else {
183161
schemaErrors = schema.walk(node, rootNode, at, state.isValidationEnabled());
184162
}
185163

186-
187164
// check if any validation errors have occurred
188165
if (schemaErrors.isEmpty()) {
189166
// check whether there are no errors HOWEVER we have validated the exact validator
190167
if (!state.hasMatchedNode())
191168
continue;
192-
else
193-
numberOfValidSchema++;
169+
170+
numberOfValidSchema++;
194171
}
195172
childErrors.addAll(schemaErrors);
196173
}
174+
175+
197176
// ensure there is always an "OneOf" error reported if number of valid schemas is not equal to 1.
198-
if (numberOfValidSchema > 1) {
199-
// check if the parent schema declares the fields as nullable
200-
if (!JsonType.NULL.equals(TypeFactory.getValueNodeType(node, this.validationContext.getConfig())) ||
201-
!JsonNodeUtil.isNodeNullable(parentSchema.getSchemaNode(), this.validationContext.getConfig()) &&
202-
!JsonNodeUtil.isChildNodeNullable((ArrayNode) schemaNode, this.validationContext.getConfig())) {
203-
final ValidationMessage message = getMultiSchemasValidErrorMsg(at);
204-
if (failFast) {
205-
throw new JsonSchemaException(message);
206-
}
207-
errors.add(message);
177+
if(numberOfValidSchema > 1){
178+
final ValidationMessage message = getMultiSchemasValidErrorMsg(at);
179+
if( failFast ) {
180+
throw new JsonSchemaException(message);
208181
}
182+
errors.add(message);
209183
}
210184
// ensure there is always an "OneOf" error reported if number of valid schemas is not equal to 1.
211185
else if (numberOfValidSchema < 1) {
@@ -262,7 +236,7 @@ private ValidationMessage getMultiSchemasValidErrorMsg(String at){
262236
}
263237

264238
return ValidationMessage.of(getValidatorType().getValue(), ValidatorTypeCode.ONE_OF ,
265-
at, String.format("but more than one schemas {%s} are valid ",msg));
239+
at, String.format("but more than one schemas {%s} are valid ",msg));
266240
}
267241

268242
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.networknt.schema;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import org.junit.jupiter.api.Assertions;
6+
import org.junit.jupiter.api.Test;
7+
8+
import java.io.InputStream;
9+
import java.util.Set;
10+
11+
public class Issue456Test {
12+
13+
protected JsonSchema getJsonSchemaFromStreamContentV7(InputStream schemaContent) {
14+
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
15+
return factory.getSchema(schemaContent);
16+
}
17+
18+
protected JsonNode getJsonNodeFromStreamContent(InputStream content) throws Exception {
19+
ObjectMapper mapper = new ObjectMapper();
20+
return mapper.readTree(content);
21+
}
22+
23+
@Test
24+
public void shouldWorkT2() throws Exception {
25+
String schemaPath = "/schema/issue456-v7.json";
26+
String dataPath = "/data/issue456-T2.json";
27+
String dataT3Path = "/data/issue456-T3.json";
28+
InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
29+
JsonSchema schema = getJsonSchemaFromStreamContentV7(schemaInputStream);
30+
InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
31+
JsonNode node = getJsonNodeFromStreamContent(dataInputStream);
32+
Set<ValidationMessage> errors = schema.validate(node);
33+
Assertions.assertEquals(0, errors.size());
34+
}
35+
36+
@Test
37+
public void shouldWorkT3() throws Exception {
38+
String schemaPath = "/schema/issue456-v7.json";
39+
String dataPath = "/data/issue456-T3.json";
40+
InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
41+
JsonSchema schema = getJsonSchemaFromStreamContentV7(schemaInputStream);
42+
InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
43+
JsonNode node = getJsonNodeFromStreamContent(dataInputStream);
44+
Set<ValidationMessage> errors = schema.validate(node);
45+
Assertions.assertEquals(0, errors.size());
46+
}
47+
48+
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"id": "abc",
3+
"details": {
4+
"__typename": "T2",
5+
"name": "Bob"
6+
}
7+
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"id": "def",
3+
"details": {
4+
"__typename": "T3",
5+
"description": "Something"
6+
}
7+
}
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://example.com/issue-456.json",
4+
"title": "OneOf validates first child only",
5+
"description": "Test description",
6+
"type": "object",
7+
"properties": {
8+
"id": "string",
9+
"details": {
10+
"oneOf": [
11+
{
12+
"type": "object",
13+
"properties": {
14+
"name": {
15+
"type": "string"
16+
},
17+
"__typename": {
18+
"const": "T2"
19+
}
20+
},
21+
"required": [
22+
"name",
23+
"__typename"
24+
]
25+
},
26+
{
27+
"type": "object",
28+
"properties": {
29+
"description": {
30+
"type": "string"
31+
},
32+
"__typename": {
33+
"const": "T3"
34+
}
35+
},
36+
"required": [
37+
"description",
38+
"__typename"
39+
]
40+
}
41+
]
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)