Step 1. Add the JitPack repository to your build file
Add it in your root settings.gradle at the end of repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
Add it in your settings.gradle.kts at the end of repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
Add to pom.xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
Add it in your build.sbt at the end of resolvers:
resolvers += "jitpack" at "https://jitpack.io"
Add it in your project.clj at the end of repositories:
:repositories [["jitpack" "https://jitpack.io"]]
Step 2. Add the dependency
dependencies {
implementation 'com.github.mrwilby:json-schema:1.5.1'
}
dependencies {
implementation("com.github.mrwilby:json-schema:1.5.1")
}
<dependency>
<groupId>com.github.mrwilby</groupId>
<artifactId>json-schema</artifactId>
<version>1.5.1</version>
</dependency>
libraryDependencies += "com.github.mrwilby" % "json-schema" % "1.5.1"
:dependencies [[com.github.mrwilby/json-schema "1.5.1"]]
This project is an implementation of the JSON Schema Core Draft v4 specification. It uses the org.json API (created by Douglas Crockford) for representing JSON data.
Lets assume that you already know what JSON Schema is, and you want to utilize it in a Java application to validate JSON data. But - as you may have already discovered - there is also an other Java implementation of the JSON Schema specification. So here are some advices about which one to use:
Add the following to your pom.xml
:
<dependency>
<groupId>org.everit.json</groupId>
<artifactId>org.everit.json.schema</artifactId>
<version>1.5.1</version>
</dependency>
If you are looking for a version which works on Java7, then you can use this artifact, kindly backported by Doctusoft:
<dependency>
<groupId>com.doctusoft</groupId>
<artifactId>json-schema-java7</artifactId>
<version>1.4.1</version>
</dependency>
import org.everit.json.schema.Schema;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.json.JSONTokener;
// ...
try (InputStream inputStream = getClass().getResourceAsStream("/path/to/your/schema.json")) {
JSONObject rawSchema = new JSONObject(new JSONTokener(inputStream));
Schema schema = SchemaLoader.load(rawSchema);
schema.validate(new JSONObject("{\"hello\" : \"world\"}")); // throws a ValidationException if this object is invalid
}
Starting from version 1.1.0
the validator collects every schema violations (instead of failing immediately on the first
one). Each failure is denoted by a JSON pointer, pointing from the root of the document to the violating part. If more
than one schema violations have been detected, then a ValidationException
will be thrown at the most common parent
elements of the violations, and each separate violations can be obtained using the ValidationException#getCausingExceptions()
method.
To demonstrate the above concepts, lets see an example. Lets consider the following schema:
{
"type" : "object",
"properties" : {
"rectangle" : {"$ref" : "#/definitions/Rectangle" }
},
"definitions" : {
"size" : {
"type" : "number",
"minimum" : 0
},
"Rectangle" : {
"type" : "object",
"properties" : {
"a" : {"$ref" : "#/definitions/size"},
"b" : {"$ref" : "#/definitions/size"}
}
}
}
}
The following JSON document has only one violation against the schema (since "a" cannot be negative):
{
"rectangle" : {
"a" : -5,
"b" : 5
}
}
In this case the thrown ValidationException
will point to #/rectangle/a
and it won't contain sub-exceptions:
try {
schema.validate(rectangleSingleFailure);
} catch (ValidationException e) {
// prints #/rectangle/a: -5.0 is not higher or equal to 0
System.out.println(e.getMessage());
}
Now - to illustrate the way how multiple violations are handled - lets consider the following JSON document, where both the "a" and "b" properties violate the above schema:
{
"rectangle" : {
"a" : -5,
"b" : "asd"
}
}
In this case the thrown ValidationException
will point to #/rectangle
, and it has 2 sub-exceptions, pointing to
#/rectangle/a
and #/rectangle/b
:
try {
schema.validate(rectangleMultipleFailures);
} catch (ValidationException e) {
System.out.println(e.getMessage());
e.getCausingExceptions().stream()
.map(ValidationException::getMessage)
.forEach(System.out::println);
}
This will print the following output:
#/rectangle: 2 schema violations found
#/rectangle/a: -5.0 is not higher or equal to 0
#/rectangle/b: expected type: Number, found: String
Since version 1.4.0
it is possible to print the ValidationException
instances as
JSON-formatted failure reports. The ValidationException#toJSON()
method returns a JSONObject
instance with the
following keys:
"message"
: the programmer-friendly exception message (desription of the validation failure)"keyword"
: the JSON Schema keyword which was violated"pointerToViolation"
: a JSON Pointer denoting the path from the input document root to its fragment which caused
the validation failure"causingExceptions"
: a (possibly empty) array of sub-exceptions. Each sub-exception is represented as a JSON object,
with the same structure as described in this listing. See more above about causing exceptions.Starting from version 1.2.0
the library supports the "format"
keyword
(which is an optional part of the specification), so you can use the following formats in the schemas:
The library also supports adding custom format validators. To use a custom validator basically you have to
org.everit.json.schema.FormatValidator
interfaceorg.everit.json.schema.loader.SchemaLoader.SchemaLoaderBuilder
instance before loading the actual schemaLets assume the task is to create a custom validator which accepts strings with an even number of characters.
The custom FormatValidator
will look something like this:
public class EvenCharNumValidator implements FormatValidator {
@Override
public Optional<String> validate(final String subject) {
if (subject.length() % 2 == 0) {
return Optional.empty();
} else {
return Optional.of(String.format("the length of srtring [%s] is odd", subject));
}
}
}
To bind the EvenCharNumValidator
to a "format"
value (for example "evenlength"
) you have to bind a validator instance
to the keyword in the schema loader configuration:
JSONObject rawSchema = new JSONObject(new JSONTokener(inputStream));
SchemaLoader schemaLoader = SchemaLoader.builder()
.schemaJson(rawSchema) // rawSchema is the JSON representation of the schema utilizing the "evenlength" non-standard format
.addFormatValidator("evenlength", new EvenCharNumValidator()) // the EvenCharNumValidator gets bound to the "evenlength" keyword
.build();
Schema schema = schemaLoader.load().build(); // the schema is created using the above created configuration
schema.validate(jsonDcoument); // the document validation happens here
In a JSON Schema document it is possible to use relative URIs to refer previously defined
types. Such references are expressed using the "$ref"
and "id"
keywords. While the specification describes resolution scope alteration and dereferencing in detail, it doesn't explain the expected behavior when the first occuring "$ref"
or "id"
is a relative URI.
In the case of this implementation it is possible to explicitly define an absolute URI serving as the base URI (resolution scope) using the appropriate builder method:
SchemaLoader schemaLoader = SchemaLoader.builder()
.schemaJson(jsonSchema)
.resolutionScope("http://example.org/") // setting the default resolution scope
.build();