Skip to content

Commit 0816c2d

Browse files
committed
Fixes #185 Validation issue in oneOf when elements have optional fields
1 parent 8364aad commit 0816c2d

File tree

3 files changed

+67
-44
lines changed

3 files changed

+67
-44
lines changed

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

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,8 @@ public OneOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
131131
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
132132
debug(logger, node, rootNode, at);
133133

134-
// this validator considers a missing node as an error
135-
// set it here to true, however re-set it to its original value upon finishing the validation
136-
boolean missingNodeAsError = config.isMissingNodeAsError();
137-
config.setMissingNodeAsError(false);
134+
// this is a complex validator, we set the flag to true
135+
config.setComplexValidator(true);
138136

139137
int numberOfValidSchema = 0;
140138
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
@@ -149,33 +147,28 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
149147
for (ShortcutValidator validator : schemas) {
150148
if (!validator.allConstantsMatch(node)) {
151149
// take a shortcut: if there is any constant that does not match,
152-
// we can bail out
150+
// we can bail out of the validation
153151
continue;
154152
}
153+
154+
// get the current validator
155155
JsonSchema schema = validator.schema;
156-
Set<ValidationMessage> schemaErrors = schema.validate(node, rootNode, at);
156+
Set<ValidationMessage> schemaErrors = schema.validate(node, rootNode, at);
157+
158+
// check if any validation errors have occurred
157159
if (schemaErrors.isEmpty()) {
160+
// check whether there are no errors HOWEVER we have validated the exact validator
161+
if(!config.hasMatchedNode())
162+
continue;
163+
158164
numberOfValidSchema++;
159165
errors = new LinkedHashSet<ValidationMessage>();
160-
161-
// a valid schema has been identified, and the node was an object, break the loop
162-
if(node.isObject())
163-
break;
164166
} else {
165-
if(node.isObject()) {
166-
if(numberOfValidSchema == 0){
167-
errors.addAll(schemaErrors);
168-
}
169-
170-
break;
171-
}
172-
}
173-
174-
if(numberOfValidSchema == 0){
175-
errors.addAll(schemaErrors);
176-
}
167+
errors.addAll(schemaErrors);
168+
}
177169
}
178170

171+
// no valid schema has been found after validating all schema validators
179172
if (numberOfValidSchema == 0) {
180173
for (Iterator<ValidationMessage> it = errors.iterator(); it.hasNext();) {
181174
ValidationMessage msg = it.next();
@@ -188,16 +181,15 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
188181
// ensure there is always an error reported if number of valid schemas is 0
189182
errors.add(buildValidationMessage(at, ""));
190183
}
184+
} else {
185+
errors.clear();
191186
}
192187

193188
// validated upfront
194189
if (numberOfValidSchema > 1) {
195190
errors = Collections.singleton(buildValidationMessage(at, ""));
196191
}
197192

198-
// reset the flag for error handling
199-
config.setMissingNodeAsError(missingNodeAsError);
200-
201193
return Collections.unmodifiableSet(errors);
202194
}
203195

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

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,33 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
4646
JsonNode propertyNode = node.get(entry.getKey());
4747

4848
if (propertyNode != null) {
49+
// check whether this is a complex validator. save the state
50+
boolean isComplex = config.isComplexValidator();
51+
// if this is a complex validator, the node has matched, and all it's child elements, if available, are to be validated
52+
if(config.isComplexValidator()) {
53+
config.setMatchedNode(true);
54+
}
55+
// reset the complex validator for child element validation, and reset it after the return from the recursive call
56+
config.setComplexValidator(false);
57+
//validate the child element(s)
4958
errors.addAll(propertySchema.validate(propertyNode, rootNode, at + "." + entry.getKey()));
59+
// reset the complex flag to the original value before the recursive call
60+
config.setComplexValidator(isComplex);
61+
// if this was a complex validator, the node has matched and has been validated
62+
if(config.isComplexValidator()) {
63+
config.setMatchedNode(true);
64+
}
5065
} else {
51-
// if a node could not be found, treat is as error/continue, depending on the SchemaValidatorsConfig
52-
//if(config.isMissingNodeAsError()) {
53-
if(getParentSchema().hasRequiredValidator())
54-
errors.addAll(getParentSchema().getRequiredValidator().validate(node, rootNode, at));
55-
// else
56-
// errors.add(buildValidationMessage(at, node.toString()));
57-
//}
66+
// decide which behavior to eomploy when validator has not matched
67+
if(config.isComplexValidator()) {
68+
// this was a complex validator (ex oneOf) and the node has not been matched
69+
config.setMatchedNode(false);
70+
return Collections.unmodifiableSet(new LinkedHashSet<ValidationMessage>());
71+
}
72+
73+
// check whether the node which has not matched was mandatory or not
74+
if(getParentSchema().hasRequiredValidator())
75+
errors.addAll(getParentSchema().getRequiredValidator().validate(node, rootNode, at));
5876
}
5977
}
6078

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

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,17 @@ public class SchemaValidatorsConfig {
2626
private boolean typeLoose;
2727

2828
/**
29-
* if IS_MISSING_NODE_AS_ERROR = true, the validator will ignore the missing node.
30-
* if set to false, then the validator will report an error
29+
* Flag set when a node has matched
30+
* Works in conjunction with the next flag: isComplexValidator, to be used for complex validators such as oneOf, for ex
3131
*/
32-
private boolean missingNodeAsError = false;
33-
32+
private boolean matchedNode = true;
33+
34+
/**
35+
* Flag set if complex validators such as oneOf, for ex, neeed to have their properties validated.
36+
* The PropertiesValidator is not aware generally of a complex validator is being validated or a simple poperty tree
37+
*/
38+
private boolean isComplexValidator = false;
39+
3440
/**
3541
* Map of public, normally internet accessible schema URLs to alternate locations; this allows for offline
3642
* validation of schemas that refer to public URLs. This is merged with any mappings the {@link JsonSchemaFactory}
@@ -65,14 +71,6 @@ public void setUriMappings(Map<String, String> uriMappings) {
6571
this.uriMappings = uriMappings;
6672
}
6773

68-
public boolean isMissingNodeAsError() {
69-
return missingNodeAsError;
70-
}
71-
72-
public void setMissingNodeAsError(boolean missingNodeAsError) {
73-
this.missingNodeAsError = missingNodeAsError;
74-
}
75-
7674
public boolean isHandleNullableField() {
7775
return handleNullableField;
7876
}
@@ -89,4 +87,19 @@ private void loadDefaultConfig() {
8987
this.typeLoose = true;
9088
this.uriMappings = new HashMap<String, String>();
9189
}
90+
91+
public void setMatchedNode(boolean matchedNode) {
92+
this.matchedNode = matchedNode;
93+
}
94+
public boolean hasMatchedNode() {
95+
return matchedNode;
96+
}
97+
98+
public boolean isComplexValidator() {
99+
return isComplexValidator;
100+
}
101+
102+
public void setComplexValidator(boolean isComplexValidator) {
103+
this.isComplexValidator = isComplexValidator;
104+
}
92105
}

0 commit comments

Comments
 (0)