diff --git a/CHANGELOG.md b/CHANGELOG.md index 337f0fa87..3974a7ec3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed +## 1.0.45 - 2020-11-21 + +### Added + +### Changed + +- fixes #350 Add builder method that accepts iterable Thanks @wheelerlaw +- fixes #347 NPE at JsonSchema.combineCurrentUriWithIds(JsonSchema.java:90) Thanks @wheelerlaw +- fixes #346 Update docs about javaSemantics flag Thanks @oguzhanunlu +- fixes #345 optimize imports in the src folder +- fixes #343 Improve type validation of numeric values Thanks @oguzhanunlu +- fixes #341 Add contentMediaType, contentEncoding and examples as a NonValidationKeyword Thanks @jonnybbb +- fixes #337 JSON Schema Walk Changes Thanks @prashanthjos + ## 1.0.44 - 2020-10-20 ### Added diff --git a/README.md b/README.md index 542822859..6c1fb3974 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Maven: com.networknt json-schema-validator - 1.0.43 + 1.0.45 ``` @@ -90,7 +90,7 @@ Gradle: ``` dependencies { - compile(group: "com.networknt", name: "json-schema-validator", version: "1.0.43"); + compile(group: "com.networknt", name: "json-schema-validator", version: "1.0.45"); } ``` @@ -126,6 +126,12 @@ I have just updated the test suites from the [official website](https://github.c [#5](https://github.com/networknt/json-schema-validator/issues/5) +## Projects + +The [light-rest-4j](https://github.com/networknt/light-rest-4j), [light-graphql-4j](https://github.com/networknt/light-graphql-4j) and [light-hybrid-4j](https://github.com/networknt/light-hybrid-4j) use this library to validate the request and response based on the specifications. If you are using other frameworks like Spring Boot, you can use the [OpenApiValidator](https://github.com/mservicetech/openapi-schema-validation), a generic OpenAPI 3.0 validator based on the OpenAPI 3.0 specification. + +If you have a project using this library, please submit a PR to add your project below. + ## Contributors Thanks to the following people who have contributed to this project. If you are using this library, please consider to be a sponsor for one of the contributors. diff --git a/doc/config.md b/doc/config.md index d8e0f6305..be7d66a2a 100644 --- a/doc/config.md +++ b/doc/config.md @@ -48,3 +48,10 @@ may have been built. The type for this variable is Map. +* javaSemantics + +When set to true, use Java-specific semantics rather than native JavaScript semantics. + +For example, if the node type is `number` per JS semantics where the value can be losslesly interpreted as `java.lang.Long`, the validator would use `integer` as the node type instead of `number`. This is useful when schema type is `integer`, since validation would fail otherwise. + +For more details, please refer to this [issue](https://github.com/networknt/json-schema-validator/issues/334). diff --git a/doc/yaml-line-numbers.md b/doc/yaml-line-numbers.md new file mode 100644 index 000000000..0fa48d399 --- /dev/null +++ b/doc/yaml-line-numbers.md @@ -0,0 +1,303 @@ +# Obtaining YAML Line Numbers + +## Scenario 1 - finding YAML line numbers from the JSON tree + +A great feature of json-schema-validator is it's ability to validate YAML documents against a JSON Scheme. The manner in which this is done though, by pre-processing the YAML into a tree of [JsonNode](https://fasterxml.github.io/jackson-databind/javadoc/2.10/com/fasterxml/jackson/databind/JsonNode.html) objects, breaks the connection back to the original YAML source file. Very commonly, once the YAML has been validated against the schema, there may be additional processing and checking for semantic or content errors or inconsistency in the JSON tree. From an end user point of view, the ideal is to report such errors using line and column references back to the original YAML, but this information is not readily available from the processed JSON tree. + +### Scenario 1, solution part 1 - capturing line details during initial parsing + +One solution is to use a custom [JsonNodeFactory](https://fasterxml.github.io/jackson-databind/javadoc/2.10/com/fasterxml/jackson/databind/node/JsonNodeFactory.html) that returns custom JsonNode objects which are created during initial parsing, and which record the original YAML locations that were being parsed at the time they were created. The example below shows this + +``` + public static class MyNodeFactory extends JsonNodeFactory + { + YAMLParser yp; + + public MyNodeFactory(YAMLParser yp) + { + super(); + this.yp = yp; + } + + public ArrayNode arrayNode() + { + return new MyArrayNode(this, yp.getTokenLocation(), yp.getCurrentLocation()); + } + + public BooleanNode booleanNode(boolean v) + { + return new MyBooleanNode(v, yp.getTokenLocation(), yp.getCurrentLocation()); + } + + public NumericNode numberNode(int v) + { + return new MyIntNode(v, yp.getTokenLocation(), yp.getCurrentLocation()); + } + + public NullNode nullNode() + { + return new MyNullNode(yp.getTokenLocation(), yp.getCurrentLocation()); + } + + public ObjectNode objectNode() + { + return new MyObjectNode(this, yp.getTokenLocation(), yp.getCurrentLocation()); + } + + public TextNode textNode(String text) + { + return (text != null) ? new MyTextNode(text, yp.getTokenLocation(), yp.getCurrentLocation()) : null; + } + } +``` + +The example above includes a basic, but usable subset of all possible JsonNode types - if your YAML needs them, than you should also consider the others i.e. `byte`, `byte[]`, `raw`, `short`, `long`, `float`, `double`, `BigInteger`, `BigDecimal` + +There are some important other things to note from the example: + +* Even in a reduced set, `ObjectNode` and `NullNode` should be included +* The current return for methods that receive a null parameter value seems to be null rather than `NullNode` (based on inspecting the underlying `valueOf()` methods in the various `JsonNode` sub classes). Hence the implementation of the `textNode()` method above. + +The actual work here is really being done by the YAMLParser - it holds the location of the token being parsed, and the current location in the file. The first of these gives us a line and column number we can use to flag where an error or problem was found, and the second (if needed) can let us calculate a span to the end of the error e.g. if we wanted to highlight or underline the text in error. + +### Scenario 1, solution part 2 - augmented `JsonNode` subclassess + +We can be as simple or fancy as we like in the `JsonNode` subclassses, but basically we need 2 pieces of information from them: + +* An interface so when we are post processing the JSON tree, we can recognize nodes that retain line number information +* An interface that lets us extract the relevant location information + +Those could be the same thing of course, but in our case we separated them as shown in the following example + +``` + public interface LocationProvider + { + LocationDetails getLocationDetails(); + } + + public interface LocationDetails + { + default int getLineNumber() { return 1; } + default int getColumnNumber() { return 1; } + default String getFilename() { return ""; } + } + + public static class LocationDetailsImpl implements LocationDetails + { + final JsonLocation currentLocation; + final JsonLocation tokenLocation; + + public LocationDetailsImpl(JsonLocation tokenLocation, JsonLocation currentLocation) + { + this.tokenLocation = tokenLocation; + this.currentLocation = currentLocation; + } + + @Override + public int getLineNumber() { return (tokenLocation != null) ? tokenLocation.getLineNr() : 1; }; + @Override + public int getColumnNumber() { return (tokenLocation != null) ? tokenLocation.getColumnNr() : 1; }; + @Override + public String getFilename() { return (tokenLocation != null) ? tokenLocation.getSourceRef().toString() : ""; }; + } + + public static class MyNullNode extends NullNode implements LocationProvider + { + final LocationDetails locDetails; + + public MyNullNode(JsonLocation tokenLocation, JsonLocation currentLocation) + { + super(); + locDetails = new LocationDetailsImpl(tokenLocation, currentLocation); + } + + @Override + public LocationDetails getLocationDetails() + { + return locDetails; + } + } + + public static class MyTextNode extends TextNode implements LocationProvider + { + final LocationDetails locDetails; + + public MyTextNode(String v, JsonLocation tokenLocation, JsonLocation currentLocation) + { + super(v); + locDetails = new LocationDetailsImpl(tokenLocation, currentLocation); + } + + @Override + public LocationDetails getLocationDetails() { return locDetails;} + } + + public static class MyIntNode extends IntNode implements LocationProvider + { + final LocationDetails locDetails; + + public MyIntNode(int v, JsonLocation tokenLocation, JsonLocation currentLocation) + { + super(v); + locDetails = new LocationDetailsImpl(tokenLocation, currentLocation); + } + + @Override + public LocationDetails getLocationDetails() { return locDetails;} + } + + public static class MyBooleanNode extends BooleanNode implements LocationProvider + { + final LocationDetails locDetails; + + public MyBooleanNode(boolean v, JsonLocation tokenLocation, JsonLocation currentLocation) + { + super(v); + locDetails = new LocationDetailsImpl(tokenLocation, currentLocation); + } + + @Override + public LocationDetails getLocationDetails() { return locDetails;} + } + + public static class MyArrayNode extends ArrayNode implements LocationProvider + { + final LocationDetails locDetails; + + public MyArrayNode(JsonNodeFactory nc, JsonLocation tokenLocation, JsonLocation currentLocation) + { + super(nc); + locDetails = new LocationDetailsImpl(tokenLocation, currentLocation); + } + + @Override + public LocationDetails getLocationDetails() { return locDetails;} + } + + public static class MyObjectNode extends ObjectNode implements LocationProvider + { + final LocationDetails locDetails; + + public MyObjectNode(JsonNodeFactory nc, JsonLocation tokenLocation, JsonLocation currentLocation) + { + super(nc); + locDetails = new LocationDetailsImpl(tokenLocation, currentLocation); + } + + @Override + public LocationDetails getLocationDetails() { return locDetails;} + } +``` + +### Scenario 1, solution part 3 - using the custom `JsonNodeFactory` + +With the pieces we now have, we just need to tell the YAML library to make of use them, which involves a minor and simple modification to the normal sequence of processing. + +``` + this.yamlFactory = new YAMLFactory(); + + try (YAMLParser yp = yamlFactory.createParser(f);) + { + ObjectReader rdr = mapper.reader(new MyNodeFactory(yp)); + JsonNode jsonNode = rdr.readTree(yp); + Set msgs = mySchema.validate(jsonNode); + + if (msgs.isEmpty()) + { + for (JsonNode item : jsonNode.get("someItem")) + { + processJsonItems(item); + } + } + else + { + // ... we'll look at how to get line locations for ValidationMessage cases in Scenario 2 + } + + } + // a JsonProcessingException seems to be the base exception for "gross" errors e.g. + // missing quotes at end of string etc. + catch (JsonProcessingException jpEx) + { + JsonLocation loc = jpEx.getLocation(); + // ... do something with the loc details + } +``` +Some notes on what is happening here: + +* We instantiate our custom JsonNodeFactory with the YAMLParser reference, and the line locations get recorded for us as the file is parsed. +* If any exceptions are thrown, they will already contain a JsonLocation object that we can use directly if needed +* If we get no validation messages, we know the JSON tree matches the schema and we can do any post processing we need on the tree. We'll see how to report any issues with this in the next part +* We'll look at how to get line locations for ValidationMessage errors in Scenario 2 + +### Scenario 1, solution part 4 - extracting the line details + +Having got everything prepared, actually getting the line locations is rather easy + + +``` + void processJsonItems(JsonNode item) + { + Iterator> iter = item.fields(); + + while (iter.hasNext()) + { + Map.Entry node = iter.next(); + extractErrorLocation(node.getValue()); + } + } + + void extractErrorLocation(JsonNode node) + { + if (node == null || !(node instanceof LocationProvider)) { return; } + + //Note: we also know the "span" of the error section i.e. from token location to current location (first char after the token) + // if we wanted at some stage we could use this to highlight/underline all of the text in error + LocationDetails dets = ((LocationProvider) node).getLocationDetails(); + // ... do something with the details e.g. report an error/issue against the YAML line + } +``` + +So that's pretty much it - as we are processing the JSON tree, if there is any point we want to report something about the contents, we can do so with a reference back to the original YAML line number. + +There is still a problem though, what if the validation against the schema fails? + +## Scenario 2 - ValidationMessage line locations + +Any failures validation against the schema come back in the form of a set of `ValidationMessage` objects. But these also do not contain original YAML source line information, and there's no easy way to inject it as we did for Scenario 1. Luckily though, there is a trick we can use here! + +Within the `ValidationMessage` object is something called the 'path' of the error, which we can access with the `getPath()` method. The syntax of this path is not exactly the same as a regular [JsonPointer](https://fasterxml.github.io/jackson-core/javadoc/2.10/com/fasterxml/jackson/core/JsonPointer.html) object, but it is sufficiently close as to be convertible. And, once converted, we can use that pointer for locating the appropriate `JsonNode`. The following couple of methods can be used to automate this process + +``` + JsonNode findJsonNode(ValidationMessage msg, JsonNode rootNode) + { + // munge the ValidationMessage path + String pathStr = StringUtils.replace(msg.getPath(), "$.", "/", 1); + pathStr = StringUtils.replace(pathStr, ".", "/"); + pathStr = StringUtils.replace(pathStr, "[", "/"); + pathStr = StringUtils.replace(pathStr, "]", ""); // array closure superfluous + JsonPointer pathPtr = JsonPointer.valueOf(pathStr); + // Now see if we can find the node + JsonNode node = rootNode.at(pathPtr); + return node; + } + + LocationDetails getLocationDetails(ValidationMessage msg, JsonNode rootNode) + { + LocationDetails retval = null; + JsonNode node = findJsonNode(msg, rootNode); + if (node != null && node instanceof LocationProvider) + { + retval = ((LocationProvider) node).getLocationDetails(); + } + return retval; + } +``` + +## Summary + +Although not trivial, the steps outlined here give us a way to track back to the original source YAML for a variety of possible reporting cases: + +* JSON processing exceptions (mostly already done for us) +* Issues flagged during validation of the YAML against the schema +* Anything we need to report with source information during post processing of the validated JSON tree diff --git a/pom.xml b/pom.xml index e7be094fc..039c421d3 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 com.networknt json-schema-validator - 1.0.44 + 1.0.45 bundle A json schema validator that supports draft v4, v6, v7 and v2019-09 https://github.com/networknt/json-schema-validator diff --git a/src/main/java/com/networknt/schema/AbstractJsonValidator.java b/src/main/java/com/networknt/schema/AbstractJsonValidator.java index bb84e94c4..c8196a87f 100644 --- a/src/main/java/com/networknt/schema/AbstractJsonValidator.java +++ b/src/main/java/com/networknt/schema/AbstractJsonValidator.java @@ -16,12 +16,12 @@ package com.networknt.schema; +import com.fasterxml.jackson.databind.JsonNode; + import java.util.Collections; import java.util.Map; import java.util.Set; -import com.fasterxml.jackson.databind.JsonNode; - public abstract class AbstractJsonValidator implements JsonValidator { private final String keyword; diff --git a/src/main/java/com/networknt/schema/BaseJsonValidator.java b/src/main/java/com/networknt/schema/BaseJsonValidator.java index b2f54f77a..84607ed77 100644 --- a/src/main/java/com/networknt/schema/BaseJsonValidator.java +++ b/src/main/java/com/networknt/schema/BaseJsonValidator.java @@ -16,14 +16,13 @@ package com.networknt.schema; -import java.net.URI; -import java.util.LinkedHashSet; -import java.util.Set; - +import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; -import com.fasterxml.jackson.databind.JsonNode; +import java.net.URI; +import java.util.LinkedHashSet; +import java.util.Set; public abstract class BaseJsonValidator implements JsonValidator { protected String schemaPath; diff --git a/src/main/java/com/networknt/schema/DateTimeValidator.java b/src/main/java/com/networknt/schema/DateTimeValidator.java index cc090a724..939ad6aeb 100644 --- a/src/main/java/com/networknt/schema/DateTimeValidator.java +++ b/src/main/java/com/networknt/schema/DateTimeValidator.java @@ -51,7 +51,7 @@ public Set validate(JsonNode node, JsonNode rootNode, String Set errors = new LinkedHashSet(); - JsonType nodeType = TypeFactory.getValueNodeType(node); + JsonType nodeType = TypeFactory.getValueNodeType(node, super.config); if (nodeType != JsonType.STRING) { return errors; } diff --git a/src/main/java/com/networknt/schema/EnumValidator.java b/src/main/java/com/networknt/schema/EnumValidator.java index 01ed2cad6..abd95f980 100644 --- a/src/main/java/com/networknt/schema/EnumValidator.java +++ b/src/main/java/com/networknt/schema/EnumValidator.java @@ -96,7 +96,7 @@ public Set validate(JsonNode node, JsonNode rootNode, String * @param node JsonNode to check */ private boolean isTypeLooseContainsInEnum(JsonNode node) { - if (TypeFactory.getValueNodeType(node) == JsonType.STRING) { + if (TypeFactory.getValueNodeType(node, super.config) == JsonType.STRING) { String nodeText = node.textValue(); for (JsonNode n : nodes) { String value = n.asText(); diff --git a/src/main/java/com/networknt/schema/ExclusiveMaximumValidator.java b/src/main/java/com/networknt/schema/ExclusiveMaximumValidator.java index 943e71f95..cf2942da1 100644 --- a/src/main/java/com/networknt/schema/ExclusiveMaximumValidator.java +++ b/src/main/java/com/networknt/schema/ExclusiveMaximumValidator.java @@ -99,7 +99,7 @@ public String thresholdValue() { public Set validate(JsonNode node, JsonNode rootNode, String at) { debug(logger, node, rootNode, at); - if (!TypeValidator.isNumber(node, config.isTypeLoose())) { + if (!TypeValidator.isNumber(node, super.config)) { // maximum only applies to numbers return Collections.emptySet(); } diff --git a/src/main/java/com/networknt/schema/ExclusiveMinimumValidator.java b/src/main/java/com/networknt/schema/ExclusiveMinimumValidator.java index cc8b6f6bb..afccbfe14 100644 --- a/src/main/java/com/networknt/schema/ExclusiveMinimumValidator.java +++ b/src/main/java/com/networknt/schema/ExclusiveMinimumValidator.java @@ -106,7 +106,7 @@ public String thresholdValue() { public Set validate(JsonNode node, JsonNode rootNode, String at) { debug(logger, node, rootNode, at); - if (!TypeValidator.isNumber(node, config.isTypeLoose())) { + if (!TypeValidator.isNumber(node, super.config)) { // minimum only applies to numbers return Collections.emptySet(); } diff --git a/src/main/java/com/networknt/schema/FormatValidator.java b/src/main/java/com/networknt/schema/FormatValidator.java index 8d8aec42b..6db5f0192 100644 --- a/src/main/java/com/networknt/schema/FormatValidator.java +++ b/src/main/java/com/networknt/schema/FormatValidator.java @@ -41,7 +41,7 @@ public Set validate(JsonNode node, JsonNode rootNode, String Set errors = new LinkedHashSet(); - JsonType nodeType = TypeFactory.getValueNodeType(node); + JsonType nodeType = TypeFactory.getValueNodeType(node, super.config); if (nodeType != JsonType.STRING) { return errors; } diff --git a/src/main/java/com/networknt/schema/JsonMetaSchema.java b/src/main/java/com/networknt/schema/JsonMetaSchema.java index adc74da27..80349193c 100644 --- a/src/main/java/com/networknt/schema/JsonMetaSchema.java +++ b/src/main/java/com/networknt/schema/JsonMetaSchema.java @@ -142,7 +142,10 @@ public static JsonMetaSchema getInstance() { new NonValidationKeyword("description"), new NonValidationKeyword("default"), new NonValidationKeyword("definitions"), - new NonValidationKeyword("$comment") + new NonValidationKeyword("$comment"), + new NonValidationKeyword("contentMediaType"), + new NonValidationKeyword("contentEncoding"), + new NonValidationKeyword("examples") )) .build(); } @@ -173,7 +176,12 @@ public static JsonMetaSchema getInstance() { new NonValidationKeyword("default"), new NonValidationKeyword("definitions"), new NonValidationKeyword("$comment"), - new NonValidationKeyword("$defs") // newly added in 2018-09 release. + new NonValidationKeyword("$defs"), // newly added in 2019-09 release. + new NonValidationKeyword("$anchor"), + new NonValidationKeyword("deprecated"), + new NonValidationKeyword("contentMediaType"), + new NonValidationKeyword("contentEncoding"), + new NonValidationKeyword("examples") )) .build(); } diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java index f1f640130..0850e46d3 100644 --- a/src/main/java/com/networknt/schema/JsonSchema.java +++ b/src/main/java/com/networknt/schema/JsonSchema.java @@ -16,24 +16,19 @@ package com.networknt.schema; +import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.schema.walk.DefaultKeywordWalkListenerRunner; +import com.networknt.schema.walk.JsonSchemaWalker; +import com.networknt.schema.walk.WalkListenerRunner; + import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.fasterxml.jackson.databind.JsonNode; -import com.networknt.schema.walk.DefaultKeywordWalkListenerRunner; -import com.networknt.schema.walk.JsonSchemaWalker; -import com.networknt.schema.walk.WalkListenerRunner; - /** * This is the core of json constraint implementation. It parses json constraint * file and generates JsonValidators. The class is thread safe, once it is @@ -100,7 +95,7 @@ private URI combineCurrentUriWithIds(URI currentUri, JsonNode schemaNode) { try { return this.validationContext.getURIFactory().create(currentUri, id); } catch (IllegalArgumentException e) { - throw new JsonSchemaException(ValidationMessage.of(ValidatorTypeCode.ID.getValue(), ValidatorTypeCode.ID, id, currentUri.toString())); + throw new JsonSchemaException(ValidationMessage.of(ValidatorTypeCode.ID.getValue(), ValidatorTypeCode.ID, id, currentUri == null ? "null" : currentUri.toString())); } } } @@ -269,7 +264,13 @@ protected ValidationResult validateAndCollect(JsonNode jsonNode, JsonNode rootNo /************************ END OF VALIDATE METHODS **********************************/ /************************ START OF WALK METHODS **********************************/ - + + /** + * Walk the JSON node + * @param node JsonNode + * @param shouldValidateSchema indicator on validation + * @return result of ValidationResult + */ public ValidationResult walk(JsonNode node, boolean shouldValidateSchema) { // Create the collector context object. CollectorContext collectorContext = new CollectorContext(); diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java index 8cd6b2674..38e873e25 100644 --- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java +++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java @@ -16,31 +16,24 @@ package com.networknt.schema; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.networknt.schema.uri.*; +import com.networknt.schema.urn.URNFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.networknt.schema.uri.ClasspathURLFactory; -import com.networknt.schema.uri.ClasspathURLFetcher; -import com.networknt.schema.uri.URIFactory; -import com.networknt.schema.uri.URIFetcher; -import com.networknt.schema.uri.URISchemeFactory; -import com.networknt.schema.uri.URISchemeFetcher; -import com.networknt.schema.uri.URLFactory; -import com.networknt.schema.uri.URLFetcher; -import com.networknt.schema.urn.URNFactory; - public class JsonSchemaFactory { private static final Logger logger = LoggerFactory .getLogger(JsonSchemaFactory.class); @@ -96,6 +89,10 @@ public Builder defaultMetaSchemaURI(final String defaultMetaSchemaURI) { * @return this builder. */ public Builder uriFactory(final URIFactory uriFactory, final String... schemes) { + return uriFactory(uriFactory, Arrays.asList(schemes)); + } + + public Builder uriFactory(final URIFactory uriFactory, final Iterable schemes) { for (final String scheme : schemes) { this.uriFactoryMap.put(scheme, uriFactory); } @@ -110,6 +107,10 @@ public Builder uriFactory(final URIFactory uriFactory, final String... schemes) * @return this builder. */ public Builder uriFetcher(final URIFetcher uriFetcher, final String... schemes) { + return uriFetcher(uriFetcher, Arrays.asList(schemes)); + } + + public Builder uriFetcher(final URIFetcher uriFetcher, final Iterable schemes) { for (final String scheme : schemes) { this.uriFetcherMap.put(scheme, uriFetcher); } diff --git a/src/main/java/com/networknt/schema/JsonValidator.java b/src/main/java/com/networknt/schema/JsonValidator.java index 48bb309da..8e9af4c6c 100644 --- a/src/main/java/com/networknt/schema/JsonValidator.java +++ b/src/main/java/com/networknt/schema/JsonValidator.java @@ -16,11 +16,11 @@ package com.networknt.schema; -import java.util.Set; - import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.walk.JsonSchemaWalker; +import java.util.Set; + /** * Standard json validator interface, implemented by all validators and JsonSchema. */ diff --git a/src/main/java/com/networknt/schema/MaxLengthValidator.java b/src/main/java/com/networknt/schema/MaxLengthValidator.java index 68ed1dad2..e5c097f71 100644 --- a/src/main/java/com/networknt/schema/MaxLengthValidator.java +++ b/src/main/java/com/networknt/schema/MaxLengthValidator.java @@ -41,7 +41,7 @@ public MaxLengthValidator(String schemaPath, JsonNode schemaNode, JsonSchema par public Set validate(JsonNode node, JsonNode rootNode, String at) { debug(logger, node, rootNode, at); - JsonType nodeType = TypeFactory.getValueNodeType(node); + JsonType nodeType = TypeFactory.getValueNodeType(node, super.config); if (nodeType != JsonType.STRING) { // ignore no-string typs return Collections.emptySet(); diff --git a/src/main/java/com/networknt/schema/MaximumValidator.java b/src/main/java/com/networknt/schema/MaximumValidator.java index 849e4d31b..b189bc7b0 100644 --- a/src/main/java/com/networknt/schema/MaximumValidator.java +++ b/src/main/java/com/networknt/schema/MaximumValidator.java @@ -108,7 +108,7 @@ public String thresholdValue() { public Set validate(JsonNode node, JsonNode rootNode, String at) { debug(logger, node, rootNode, at); - if (!TypeValidator.isNumber(node, config.isTypeLoose())) { + if (!TypeValidator.isNumber(node, super.config)) { // maximum only applies to numbers return Collections.emptySet(); } diff --git a/src/main/java/com/networknt/schema/MinLengthValidator.java b/src/main/java/com/networknt/schema/MinLengthValidator.java index 8eddebcfa..cec85eb43 100644 --- a/src/main/java/com/networknt/schema/MinLengthValidator.java +++ b/src/main/java/com/networknt/schema/MinLengthValidator.java @@ -41,7 +41,7 @@ public MinLengthValidator(String schemaPath, JsonNode schemaNode, JsonSchema par public Set validate(JsonNode node, JsonNode rootNode, String at) { debug(logger, node, rootNode, at); - JsonType nodeType = TypeFactory.getValueNodeType(node); + JsonType nodeType = TypeFactory.getValueNodeType(node, super.config); if (nodeType != JsonType.STRING) { // ignore non-string types return Collections.emptySet(); diff --git a/src/main/java/com/networknt/schema/MinimumValidator.java b/src/main/java/com/networknt/schema/MinimumValidator.java index b2f932ae7..f9af8f8be 100644 --- a/src/main/java/com/networknt/schema/MinimumValidator.java +++ b/src/main/java/com/networknt/schema/MinimumValidator.java @@ -114,7 +114,7 @@ public String thresholdValue() { public Set validate(JsonNode node, JsonNode rootNode, String at) { debug(logger, node, rootNode, at); - if (!TypeValidator.isNumber(node, config.isTypeLoose())) { + if (!TypeValidator.isNumber(node, super.config)) { // minimum only applies to numbers return Collections.emptySet(); } diff --git a/src/main/java/com/networknt/schema/OneOfValidator.java b/src/main/java/com/networknt/schema/OneOfValidator.java index 81327e04a..0ba783229 100644 --- a/src/main/java/com/networknt/schema/OneOfValidator.java +++ b/src/main/java/com/networknt/schema/OneOfValidator.java @@ -162,31 +162,12 @@ public Set validate(JsonNode node, JsonNode rootNode, String continue; numberOfValidSchema++; - errors = new LinkedHashSet(); - } else { - errors.addAll(schemaErrors); } } - // no valid schema has been found after validating all schema validators - if (numberOfValidSchema == 0) { - for (Iterator it = errors.iterator(); it.hasNext(); ) { - ValidationMessage msg = it.next(); - if (ValidatorTypeCode.ADDITIONAL_PROPERTIES.getValue().equals(msg.getType())) { - it.remove(); - } - } - if (errors.isEmpty()) { - // ensure there is always an error reported if number of valid schemas is 0 - errors.add(buildValidationMessage(at, "")); - } - } else { - errors.clear(); - } - - // validated upfront - if (numberOfValidSchema > 1) { + // ensure there is always an "OneOf" error reported if number of valid schemas is not equal to 1. + if (numberOfValidSchema != 1) { errors = Collections.singleton(buildValidationMessage(at, "")); } diff --git a/src/main/java/com/networknt/schema/PatternValidator.java b/src/main/java/com/networknt/schema/PatternValidator.java index ff7bb4f3e..6fe4c22a9 100644 --- a/src/main/java/com/networknt/schema/PatternValidator.java +++ b/src/main/java/com/networknt/schema/PatternValidator.java @@ -16,12 +16,7 @@ package com.networknt.schema; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - +import com.fasterxml.jackson.databind.JsonNode; import org.jcodings.specific.UTF8Encoding; import org.joni.Option; import org.joni.Regex; @@ -30,7 +25,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.JsonNode; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; public class PatternValidator implements JsonValidator { @@ -94,7 +93,7 @@ private boolean matches(String value) { public Set validate(JsonNode node, JsonNode rootNode, String at) { debug(logger, node, rootNode, at); - JsonType nodeType = TypeFactory.getValueNodeType(node); + JsonType nodeType = TypeFactory.getValueNodeType(node, super.config); if (nodeType != JsonType.STRING) { return Collections.emptySet(); } @@ -151,7 +150,7 @@ private boolean matches(String value) { public Set validate(JsonNode node, JsonNode rootNode, String at) { debug(logger, node, rootNode, at); - JsonType nodeType = TypeFactory.getValueNodeType(node); + JsonType nodeType = TypeFactory.getValueNodeType(node, super.config); if (nodeType != JsonType.STRING) { return Collections.emptySet(); } diff --git a/src/main/java/com/networknt/schema/PropertiesValidator.java b/src/main/java/com/networknt/schema/PropertiesValidator.java index 0a2adcc54..7041702b3 100644 --- a/src/main/java/com/networknt/schema/PropertiesValidator.java +++ b/src/main/java/com/networknt/schema/PropertiesValidator.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.walk.DefaultPropertyWalkListenerRunner; import com.networknt.schema.walk.WalkListenerRunner; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/networknt/schema/RefValidator.java b/src/main/java/com/networknt/schema/RefValidator.java index 349a16e4b..4df7c7d90 100644 --- a/src/main/java/com/networknt/schema/RefValidator.java +++ b/src/main/java/com/networknt/schema/RefValidator.java @@ -23,7 +23,6 @@ import org.slf4j.LoggerFactory; import java.net.URI; -import java.net.URISyntaxException; import java.text.MessageFormat; import java.util.Collections; import java.util.HashSet; diff --git a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java index fca239087..8fbea105b 100644 --- a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java +++ b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java @@ -16,15 +16,11 @@ package com.networknt.schema; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.walk.JsonSchemaWalkListener; +import java.util.*; + public class SchemaValidatorsConfig { /** * when validate type, if TYPE_LOOSE = true, will try to convert string to different types to match the type defined in schema. @@ -41,6 +37,11 @@ public class SchemaValidatorsConfig { */ private boolean ecma262Validator; + /** + * When set to true, use Java-specific semantics rather than native JavaScript semantics + */ + private boolean javaSemantics; + /** * Map of public, normally internet accessible schema URLs to alternate locations; this allows for offline * validation of schemas that refer to public URLs. This is merged with any mappings the {@link JsonSchemaFactory} @@ -112,7 +113,11 @@ public boolean isEcma262Validator() { public void setEcma262Validator(boolean ecma262Validator) { this.ecma262Validator = ecma262Validator; } - + + public boolean isJavaSemantics() { return javaSemantics; } + + public void setJavaSemantics(boolean javaSemantics) { this.javaSemantics = javaSemantics; } + public void addKeywordWalkListener(JsonSchemaWalkListener keywordWalkListener) { if (keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY) == null) { List keywordWalkListeners = new ArrayList(); diff --git a/src/main/java/com/networknt/schema/TypeFactory.java b/src/main/java/com/networknt/schema/TypeFactory.java index 57049fde1..e3a9645a0 100644 --- a/src/main/java/com/networknt/schema/TypeFactory.java +++ b/src/main/java/com/networknt/schema/TypeFactory.java @@ -57,7 +57,7 @@ public static JsonType getSchemaNodeType(JsonNode node) { return JsonType.UNKNOWN; } - public static JsonType getValueNodeType(JsonNode node) { + public static JsonType getValueNodeType(JsonNode node, SchemaValidatorsConfig config) { if (node.isContainerNode()) { if (node.isObject()) return JsonType.OBJECT; @@ -72,7 +72,10 @@ public static JsonType getValueNodeType(JsonNode node) { if (node.isIntegralNumber()) return JsonType.INTEGER; if (node.isNumber()) - return JsonType.NUMBER; + if (config.isJavaSemantics() && node.canConvertToLong() && (node.asText().indexOf('.') == -1)) + return JsonType.INTEGER; + else + return JsonType.NUMBER; if (node.isBoolean()) return JsonType.BOOLEAN; if (node.isNull()) diff --git a/src/main/java/com/networknt/schema/TypeValidator.java b/src/main/java/com/networknt/schema/TypeValidator.java index 154389833..3ca57db68 100644 --- a/src/main/java/com/networknt/schema/TypeValidator.java +++ b/src/main/java/com/networknt/schema/TypeValidator.java @@ -51,7 +51,7 @@ public JsonType getSchemaType() { } public boolean equalsToSchemaType(JsonNode node) { - JsonType nodeType = TypeFactory.getValueNodeType(node); + JsonType nodeType = TypeFactory.getValueNodeType(node, super.config); // in the case that node type is not the same as schema type, try to convert node to the // same type of schema. In REST API, query parameters, path parameters and headers are all // string type and we must convert, otherwise, all schema validations will fail. @@ -107,7 +107,7 @@ public Set validate(JsonNode node, JsonNode rootNode, String } if (!equalsToSchemaType(node)) { - JsonType nodeType = TypeFactory.getValueNodeType(node); + JsonType nodeType = TypeFactory.getValueNodeType(node, super.config); return Collections.singleton(buildValidationMessage(at, nodeType.toString(), schemaType.toString())); } return Collections.emptySet(); @@ -218,14 +218,14 @@ public static boolean isNumeric(String str) { * status of typeLoose flag. * * @param node the JsonNode to check - * @param isTypeLoose The flag to show whether typeLoose is enabled + * @param config the SchemaValidatorsConfig to depend on * @return boolean to indicate if it is a number */ - public static boolean isNumber(JsonNode node, boolean isTypeLoose) { + public static boolean isNumber(JsonNode node, SchemaValidatorsConfig config) { if (node.isNumber()) { return true; - } else if (isTypeLoose) { - if (TypeFactory.getValueNodeType(node) == JsonType.STRING) { + } else if (config.isTypeLoose()) { + if (TypeFactory.getValueNodeType(node, config) == JsonType.STRING) { return isNumeric(node.textValue()); } } diff --git a/src/main/java/com/networknt/schema/UUIDValidator.java b/src/main/java/com/networknt/schema/UUIDValidator.java index 7b5cd1461..2677befa7 100644 --- a/src/main/java/com/networknt/schema/UUIDValidator.java +++ b/src/main/java/com/networknt/schema/UUIDValidator.java @@ -43,7 +43,7 @@ public Set validate(JsonNode node, JsonNode rootNode, String Set errors = new LinkedHashSet(); - JsonType nodeType = TypeFactory.getValueNodeType(node); + JsonType nodeType = TypeFactory.getValueNodeType(node, super.config); if (nodeType != JsonType.STRING) { return errors; } diff --git a/src/main/java/com/networknt/schema/UnionTypeValidator.java b/src/main/java/com/networknt/schema/UnionTypeValidator.java index 446de01b4..f6f33b08f 100644 --- a/src/main/java/com/networknt/schema/UnionTypeValidator.java +++ b/src/main/java/com/networknt/schema/UnionTypeValidator.java @@ -66,7 +66,7 @@ public UnionTypeValidator(String schemaPath, JsonNode schemaNode, JsonSchema par public Set validate(JsonNode node, JsonNode rootNode, String at) { debug(logger, node, rootNode, at); - JsonType nodeType = TypeFactory.getValueNodeType(node); + JsonType nodeType = TypeFactory.getValueNodeType(node, super.config); boolean valid = false; diff --git a/src/main/java/com/networknt/schema/ValidationContext.java b/src/main/java/com/networknt/schema/ValidationContext.java index 8715d5746..d8f3a1f44 100644 --- a/src/main/java/com/networknt/schema/ValidationContext.java +++ b/src/main/java/com/networknt/schema/ValidationContext.java @@ -16,13 +16,13 @@ package com.networknt.schema; -import java.util.HashMap; -import java.util.Map; - import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.uri.URIFactory; import com.networknt.schema.urn.URNFactory; +import java.util.HashMap; +import java.util.Map; + public class ValidationContext { private final URIFactory uriFactory; private final URNFactory urnFactory; diff --git a/src/main/java/com/networknt/schema/format/EmailValidator.java b/src/main/java/com/networknt/schema/format/EmailValidator.java index 994b247d8..57e920765 100644 --- a/src/main/java/com/networknt/schema/format/EmailValidator.java +++ b/src/main/java/com/networknt/schema/format/EmailValidator.java @@ -147,7 +147,7 @@ public Set validate(JsonNode node, JsonNode rootNode, String Set errors = new LinkedHashSet(); - JsonType nodeType = TypeFactory.getValueNodeType(node); + JsonType nodeType = TypeFactory.getValueNodeType(node, super.config); if (nodeType != JsonType.STRING) { return errors; } diff --git a/src/main/java/com/networknt/schema/walk/AbstractWalkListenerRunner.java b/src/main/java/com/networknt/schema/walk/AbstractWalkListenerRunner.java index 1b0d7fd73..23631fb86 100644 --- a/src/main/java/com/networknt/schema/walk/AbstractWalkListenerRunner.java +++ b/src/main/java/com/networknt/schema/walk/AbstractWalkListenerRunner.java @@ -1,13 +1,13 @@ package com.networknt.schema.walk; -import java.util.List; -import java.util.Set; - import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.ValidationMessage; +import java.util.List; +import java.util.Set; + public abstract class AbstractWalkListenerRunner implements WalkListenerRunner { protected String getKeywordName(String keyWordPath) { diff --git a/src/main/java/com/networknt/schema/walk/DefaultKeywordWalkListenerRunner.java b/src/main/java/com/networknt/schema/walk/DefaultKeywordWalkListenerRunner.java index 834175582..103e5ff6a 100644 --- a/src/main/java/com/networknt/schema/walk/DefaultKeywordWalkListenerRunner.java +++ b/src/main/java/com/networknt/schema/walk/DefaultKeywordWalkListenerRunner.java @@ -1,15 +1,15 @@ package com.networknt.schema.walk; -import java.util.List; -import java.util.Map; -import java.util.Set; - import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SchemaValidatorsConfig; import com.networknt.schema.ValidationMessage; +import java.util.List; +import java.util.Map; +import java.util.Set; + public class DefaultKeywordWalkListenerRunner extends AbstractWalkListenerRunner { private Map> keywordWalkListenersMap; diff --git a/src/main/java/com/networknt/schema/walk/DefaultPropertyWalkListenerRunner.java b/src/main/java/com/networknt/schema/walk/DefaultPropertyWalkListenerRunner.java index d32960126..64006d6fc 100644 --- a/src/main/java/com/networknt/schema/walk/DefaultPropertyWalkListenerRunner.java +++ b/src/main/java/com/networknt/schema/walk/DefaultPropertyWalkListenerRunner.java @@ -1,13 +1,13 @@ package com.networknt.schema.walk; -import java.util.List; -import java.util.Set; - import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.ValidationMessage; +import java.util.List; +import java.util.Set; + public class DefaultPropertyWalkListenerRunner extends AbstractWalkListenerRunner { private List propertyWalkListeners; diff --git a/src/main/java/com/networknt/schema/walk/JsonSchemaWalkListener.java b/src/main/java/com/networknt/schema/walk/JsonSchemaWalkListener.java index 7210d138a..187387926 100644 --- a/src/main/java/com/networknt/schema/walk/JsonSchemaWalkListener.java +++ b/src/main/java/com/networknt/schema/walk/JsonSchemaWalkListener.java @@ -1,9 +1,9 @@ package com.networknt.schema.walk; -import java.util.Set; - import com.networknt.schema.ValidationMessage; +import java.util.Set; + /** * * Listener class that captures walkStart and walkEnd events. diff --git a/src/main/java/com/networknt/schema/walk/JsonSchemaWalker.java b/src/main/java/com/networknt/schema/walk/JsonSchemaWalker.java index 2508920a3..79be872ee 100644 --- a/src/main/java/com/networknt/schema/walk/JsonSchemaWalker.java +++ b/src/main/java/com/networknt/schema/walk/JsonSchemaWalker.java @@ -1,11 +1,11 @@ package com.networknt.schema.walk; -import java.util.Set; - import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.BaseJsonValidator; import com.networknt.schema.ValidationMessage; +import java.util.Set; + public interface JsonSchemaWalker { /** * diff --git a/src/main/java/com/networknt/schema/walk/WalkEvent.java b/src/main/java/com/networknt/schema/walk/WalkEvent.java index c954d1eca..97a04373d 100644 --- a/src/main/java/com/networknt/schema/walk/WalkEvent.java +++ b/src/main/java/com/networknt/schema/walk/WalkEvent.java @@ -1,11 +1,11 @@ package com.networknt.schema.walk; -import java.net.URI; - import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; +import java.net.URI; + /** * * Encapsulation of Walk data that is passed into the {@link JsonSchemaWalkListener}. diff --git a/src/main/java/com/networknt/schema/walk/WalkListenerRunner.java b/src/main/java/com/networknt/schema/walk/WalkListenerRunner.java index 3da8fe484..089cf2bf8 100644 --- a/src/main/java/com/networknt/schema/walk/WalkListenerRunner.java +++ b/src/main/java/com/networknt/schema/walk/WalkListenerRunner.java @@ -1,12 +1,12 @@ package com.networknt.schema.walk; -import java.util.Set; - import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.ValidationMessage; +import java.util.Set; + public interface WalkListenerRunner { public boolean runPreWalkListeners(String keyWordPath, JsonNode node, JsonNode rootNode, String at, diff --git a/src/test/java/com/networknt/schema/Issue285Test.java b/src/test/java/com/networknt/schema/Issue285Test.java index 5684e0263..e0416f6a3 100644 --- a/src/test/java/com/networknt/schema/Issue285Test.java +++ b/src/test/java/com/networknt/schema/Issue285Test.java @@ -1,6 +1,5 @@ package com.networknt.schema; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/com/networknt/schema/Issue347Test.java b/src/test/java/com/networknt/schema/Issue347Test.java new file mode 100644 index 000000000..d110d65d3 --- /dev/null +++ b/src/test/java/com/networknt/schema/Issue347Test.java @@ -0,0 +1,22 @@ +package com.networknt.schema; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; + +public class Issue347Test { + + @Test + public void failure() { + ObjectMapper mapper = new ObjectMapper(); + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7); + try { + JsonSchema schema = factory.getSchema(Thread.currentThread().getContextClassLoader().getResourceAsStream("schema/issue347-v7.json")); + } catch (Throwable e) { + assertThat(e, instanceOf(JsonSchemaException.class)); + assertEquals("test: null is an invalid segment for URI {2}", e.getMessage()); + } + } +} diff --git a/src/test/java/com/networknt/schema/JsonWalkTest.java b/src/test/java/com/networknt/schema/JsonWalkTest.java index 605082e20..26c902d8f 100644 --- a/src/test/java/com/networknt/schema/JsonWalkTest.java +++ b/src/test/java/com/networknt/schema/JsonWalkTest.java @@ -1,6 +1,13 @@ package com.networknt.schema; -import static org.junit.Assert.assertEquals; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.networknt.schema.walk.JsonSchemaWalkListener; +import com.networknt.schema.walk.WalkEvent; +import com.networknt.schema.walk.WalkFlow; +import org.junit.Before; +import org.junit.Test; import java.io.IOException; import java.io.InputStream; @@ -8,15 +15,7 @@ import java.util.Set; import java.util.TreeSet; -import com.networknt.schema.walk.WalkFlow; -import org.junit.Before; -import org.junit.Test; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.networknt.schema.walk.WalkEvent; -import com.networknt.schema.walk.JsonSchemaWalkListener; +import static org.junit.Assert.assertEquals; public class JsonWalkTest { diff --git a/src/test/java/com/networknt/schema/SpecVersionDetectorTest.java b/src/test/java/com/networknt/schema/SpecVersionDetectorTest.java index a2244b0f1..7f326efac 100644 --- a/src/test/java/com/networknt/schema/SpecVersionDetectorTest.java +++ b/src/test/java/com/networknt/schema/SpecVersionDetectorTest.java @@ -6,9 +6,8 @@ import java.io.IOException; import java.io.InputStream; -import java.net.URI; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; public class SpecVersionDetectorTest { diff --git a/src/test/java/com/networknt/schema/TypeFactoryTest.java b/src/test/java/com/networknt/schema/TypeFactoryTest.java new file mode 100755 index 000000000..01f15ca1a --- /dev/null +++ b/src/test/java/com/networknt/schema/TypeFactoryTest.java @@ -0,0 +1,53 @@ +/* + * 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.node.DecimalNode; +import org.junit.Test; + +import java.math.BigDecimal; + +import static com.networknt.schema.TypeFactory.getValueNodeType; +import static org.junit.Assert.assertSame; + +public class TypeFactoryTest { + + private static final String[] validIntegralValues = { + "1", "-1", "0E+1", "0E1", "-0E+1", "-0E1", "10.1E+1", "10.1E1", "-10.1E+1", "-10.1E1", "1E+0", "1E-0", + "1E0", "1E18", "9223372036854775807", "-9223372036854775808" + }; + + private final SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); + + @Test + public void testValidIntegralValuesWithJavaSemantics() { + schemaValidatorsConfig.setJavaSemantics(true); + for (String validValue : validIntegralValues) { + assertSame(validValue, JsonType.INTEGER, + getValueNodeType(DecimalNode.valueOf(new BigDecimal(validValue)), schemaValidatorsConfig)); + } + } + + @Test + public void testValidIntegralValuesWithoutJavaSemantics() { + schemaValidatorsConfig.setJavaSemantics(false); + for (String validValue : validIntegralValues) { + assertSame(validValue, JsonType.NUMBER, + getValueNodeType(DecimalNode.valueOf(new BigDecimal(validValue)), schemaValidatorsConfig)); + } + } +} diff --git a/src/test/resources/draft6/oneOf.json b/src/test/resources/draft6/oneOf.json index 65ab47d0a..c637edda0 100644 --- a/src/test/resources/draft6/oneOf.json +++ b/src/test/resources/draft6/oneOf.json @@ -189,6 +189,11 @@ "bar": "quux" }, "valid": false + }, + { + "description": "neither oneOf given (complex)", + "data": {}, + "valid": false } ] }, diff --git a/src/test/resources/schema/issue347-v7.json b/src/test/resources/schema/issue347-v7.json new file mode 100644 index 000000000..2ae91c0a7 --- /dev/null +++ b/src/test/resources/schema/issue347-v7.json @@ -0,0 +1,19 @@ +{ + "$id": "test", + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Test description", + "properties": { + "test": { + "description": "Sample property", + "examples": [ + "hello", + "world" + ], + "type": "string" + } + }, + "required": [ + "test" + ], + "type": "object" +}