Skip to content

IF-THEN-ELSE Conditional (Draft 7) #206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/main/java/com/networknt/schema/IfValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2016 Network New Technologies Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.networknt.schema;

import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public class IfValidator extends BaseJsonValidator implements JsonValidator {
private static final Logger logger = LoggerFactory.getLogger(IfValidator.class);

private static final ArrayList<String> KEYWORDS = new ArrayList<String>(Arrays.asList("if", "then", "else"));

private JsonSchema ifSchema;
private JsonSchema thenSchema;
private JsonSchema elseSchema;

public IfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.IF_THEN_ELSE, validationContext);

for (final String keyword : KEYWORDS) {
final JsonNode node = schemaNode.get(keyword);
if (keyword.equals("if")) {
ifSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
} else if (keyword.equals("then") && node != null) {
thenSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
} else if (keyword.equals("else") && node != null) {
elseSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
}
}
}

public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
debug(logger, node, rootNode, at);

Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();

Set<ValidationMessage> ifErrors = ifSchema.validate(node, rootNode, at);
if (ifErrors.isEmpty() && thenSchema != null) {
errors.addAll(thenSchema.validate(node, rootNode, at));
} else if (thenSchema != null && elseSchema != null) {
errors.addAll(elseSchema.validate(node, rootNode, at));
}

return Collections.unmodifiableSet(errors);
}

}
6 changes: 3 additions & 3 deletions src/main/java/com/networknt/schema/JsonSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {
Iterator<String> pnames = schemaNode.fieldNames();
while (pnames.hasNext()) {
String pname = pnames.next();
JsonNode n = schemaNode.get(pname);
JsonNode nodeToUse = pname.equals("if") ? schemaNode : schemaNode.get(pname);

JsonValidator validator = validationContext.newValidator(getSchemaPath(), pname, n, this);
JsonValidator validator = validationContext.newValidator(getSchemaPath(), pname, nodeToUse, this);
if (validator != null) {
validators.put(getSchemaPath() + "/" + pname, validator);

if(pname.equals("required"))
if (pname.equals("required"))
requiredValidator = validator;
}

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/networknt/schema/ValidatorTypeCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSc
UNIQUE_ITEMS("uniqueItems", "1031", new MessageFormat("{0}: the items in the array must be unique"), UniqueItemsValidator.class),
DATETIME("date-time", "1034", new MessageFormat("{0}: {1} is an invalid {2}"), null),
UUID("uuid", "1035", new MessageFormat("{0}: {1} is an invalid {2}"), null),
ID("id", "1036", new MessageFormat("{0}: {1} is an invalid segment for URI {2}"), null);
ID("id", "1036", new MessageFormat("{0}: {1} is an invalid segment for URI {2}"), null),
IF_THEN_ELSE("if", "1037", null, IfValidator.class);

private static Map<String, ValidatorTypeCode> constants = new HashMap<String, ValidatorTypeCode>();

Expand Down
7 changes: 5 additions & 2 deletions src/test/java/com/networknt/schema/JsonSchemaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,16 @@ public void testSchemaFromClasspath() throws Exception {
runTestFile("tests/classpath/schema.json");
}



@Test
public void testUUIDValidator() throws Exception {
runTestFile("tests/uuid.json");
}

@Test
public void testIfValidator() throws Exception {
runTestFile("tests/if.json");
}

/**
* Although, the data file has three errors, but only on is reported
*/
Expand Down
217 changes: 217 additions & 0 deletions src/test/resources/tests/if.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
[
{
"description": "Simple if (incomplete structure, without THEN and ELSE, no validation is performed)",
"schema": {
"then": {
"properties": {
"foo": { "maximum": 3.0 }
}
},
"else": {
"properties": {
"foo": { "maximum": 3.0 }
}
}
},
"tests": [
{
"description": "if valid",
"data": { "foo": 2.9 },
"valid": true
},
{
"description": "if invalid",
"data": { "foo": 3.1 },
"valid": true
}
]
},
{
"description": "Simple if (incomplete structure, without THEN and ELSE, no validation is performed)",
"schema": {
"if": {
"properties": {
"foo": { "maximum": 3.0 }
}
}
},
"tests": [
{
"description": "if valid",
"data": { "foo": 2.9 },
"valid": true
},
{
"description": "if invalid",
"data": { "foo": 3.1 },
"valid": true
}
]
},
{
"description": "Simple if-else (incomplete structure, without THEN, no validation is performed)",
"schema": {
"if": {
"properties": {
"foo": { "minimum": 3.0 }
}
},
"else": {
"required": [ "bar" ]
}
},
"tests": [
{
"description": "if valid, no validation (THEN is not executed because it doesn't exist)",
"data": { "foo": 3.1 },
"valid": true
},
{
"description": "if invalid, no validation (ELSE is not executed because THEN doesn't exist)",
"data": { "foo": 2.9 },
"valid": true
}
]
},
{
"description": "Simple if-then (complete structure, without ELSE)",
"schema": {
"if": {
"properties": {
"foo": { "pattern": "^[AB]$" }
}
},
"then": {
"required": [ "bar" ]
}
},
"tests": [
{
"description": "Simple if-then valid",
"data": { "foo": "A", "bar": 1 },
"valid": true
},
{
"description": "Simple if-then invalid",
"data": { "foo": "A" },
"valid": false
}
]
},
{
"description": "Simple if-then-else (complete structure)",
"schema": {
"if": {
"properties": {
"foo": { "pattern": "^[AB]$" }
}
},
"then": {
"required": [ "bar" ]
},
"else": {
"required": [ "baz" ]
}
},
"tests": [
{
"description": "Simple if-then-else, then valid",
"data": { "foo": "A", "bar": 1 },
"valid": true
},
{
"description": "Simple if-then-else, then invalid",
"data": { "foo": "A" },
"valid": false
},
{
"description": "Simple if-then-else, else valid",
"data": { "foo": "C", "baz": 1 },
"valid": true
},
{
"description": "Simple if-then-else, else invalid",
"data": { "foo": "C" },
"valid": false
}
]
},
{
"description": "Nested if-then-else (complete structure)",
"schema": {
"if": {
"properties": {
"foo": { "pattern": "^[AB]$" }
}
},
"then": {
"if": {
"properties": {
"bar": { "pattern": "^[CD]$" }
}
},
"then": {
"required": [ "baz" ]
},
"else": {
"required": [ "qux" ]
}
},
"else": {
"if": {
"properties": {
"bar": { "pattern": "^[XY]$" }
}
},
"then": {
"required": [ "baz" ]
},
"else": {
"required": [ "qux" ]
}
}
},
"tests": [
{
"description": "if-then-if-then valid",
"data": { "foo": "A", "bar": "C", "baz": 1 },
"valid": true
},
{
"description": "if-then-if-then invalid",
"data": { "foo": "A", "bar": "C" },
"valid": false
},
{
"description": "if-then-if-else valid",
"data": { "foo": "A", "bar": "E", "qux": 1 },
"valid": true
},
{
"description": "if-then-if-else invalid",
"data": { "foo": "A", "bar": "E" },
"valid": false
},
{
"description": "if-else-if-then valid",
"data": { "foo": "C", "bar": "X", "baz": 1 },
"valid": true
},
{
"description": "if-else-if-then invalid",
"data": { "foo": "C", "bar": "X" },
"valid": false
},
{
"description": "if-else-if-else valid",
"data": { "foo": "C", "bar": "Z", "qux": 1 },
"valid": true
},
{
"description": "if-else-if-else invalid",
"data": { "foo": "C", "bar": "Z" },
"valid": false
}
]
}
]