See: Description
| Package | Description |
|---|---|
| org.eel.kitchen.jsonschema.bundle |
Schema keywords and keyword bundles
|
| org.eel.kitchen.jsonschema.format |
Format specifier classes
|
| org.eel.kitchen.jsonschema.keyword |
Schema validation core elements: keyword validators
|
| org.eel.kitchen.jsonschema.main |
Main schema validation API
|
| org.eel.kitchen.jsonschema.ref |
JSON Reference related packages
|
| org.eel.kitchen.jsonschema.syntax |
Schema syntax validation package
|
| org.eel.kitchen.jsonschema.uri |
JSON document downloaders
|
| org.eel.kitchen.jsonschema.util |
Various utility packages
|
| org.eel.kitchen.jsonschema.validator |
Core validation process
|
This implementation is meant to be purely server-side if Java is your language of choice. You may, or may not, use it in your Java Web application; this library has no dependencies on anything Web-related.
This library has three main characteristics which make it stand apart from other existing implementations. While some other libraries also bear one or two of the below characteristics, none can pretend to all three of them:
JsonSchemaFactoryYou cannot instantiate one directly, you have to go through its included Builder class for that. This is by design: it allows to freeze the schema generation context. Here is the most simple way of building a factory using default settings:
// Build a schema factory with default settings
final JsonSchemaFactory factory = new JsonSchemaFactory.Builder().build();
You can customize your factory in many ways:
See below, and refer to the javadoc for more details.
The class for this is JsonSchema.
This is a three step process:
JsonNode (here, the JsonLoader will probably be useful);SchemaContainer;JsonSchema instance.The sample code below shows how to create a JsonSchema from the v3 schema draft, bundled in
this library (NOTE: IOException not accounted for):
final JsonNode rawSchema = JsonLoader.fromResource("/schema-draftv3.json");
final SchemaContainer container = factory.registerSchema(rawSchema);
final JsonSchema schema = factory.createSchema(container);
Currently, you must load the data to be validated as a JsonNode. Here again, the JsonLoader class may help you.
One JsonSchema instance may validate
as many JSON documents as you want, in parallel: this class is inherently thread
safe. Sample code:
// Here, instance is the data to validate
final ValidationReport report = schema.validate(instance);
// Validation is a success if the report is empty: test it as follows
System.out.printf("Validation %s\n", report.isSuccess() ? "succeeded"
: "failed");
First note that all customization is achieved via the associated builder: once a factory is created, it cannot be modified.
When resolving JSON References, relative URIs may be encountered. In this
case, they should be resolved against the parent schema's location. But it can
happen that the parent schema's location is empty: in this case, the URI will
not resolve. As a matter of fact, the class in charge of getting a JSON document
from an URI, URIManager, will refuse to
get content from a relative URI.
To work around this problem, you have the ability to set a default namespace: relative URIs will then be resolved with regards to that namespace. This can be useful if, for instance, you perform offline validation.
One possible scenario is to have a set of schemas on the local filesystem, and schemas refer to one another in a relative way. You can do:
// Note the ending /, it is necessary!
final JsonSchemaFactory factory = new JsonSchemaFactory.Builder()
.setNamespace("file:///path/to/base/directory/").build();
If you use schemas which reference other schemas via URIs you know will not be reachable at runtime, this mechanism allows you to tell the validation process to redirect all references to these URIs to other URIs you know you can resolve locally. For instance:
// Reference to external schema, and the local version of it
final String from = "http://external.site/the/schema.json";
final String to = "file:///path/to/local-copy.json";
final JsonSchemaFactory factory = new JsonSchemaFactory.Builder()
.addRedirection(from, to).build();
You can, of course, add more than one redirection.
For this, you need three elements:
SyntaxChecker instance;KeywordValidator.The keyword validator needs to have one constructor taking a JsonNode as an argument (keyword validators are built by reflection -- this is what allows them to be cached). You can then register the keyword to your factory like this:
final Keyword myKeyword = Keyword.Builder.forName("mykeyword")
.withSyntaxChecker(mySyntaxChecker)
.withValidatorClass(MyKeywordValidator.class)
.build();
final JsonSchemaFactory.Builder builder = new JsonSchemaFactory.Builder();
// Register your own keyword, and unregister "format"
builder.registerKeyword(myKeyword).unregisterKeyword("format");
// Build the factory
final JsonSchemaFactory factory = builder.build();
For further information, see org.eel.kitchen.jsonschema.syntax and
org.eel.kitchen.jsonschema.keyword.
A JSON Schema locator is a URI, and this URI can be of any scheme. This implementation has native support for the following schemes, which are guaranteed to be implemented by any Java implementation: http, ftp, file and jar. While https is also guaranteed to be implemented, this implementation chooses not to offer support for it, because of certificate matters, which can be quite cumbersome.
You can, if you wish, either override the existing supported schemes, or add
support for a new scheme, by implementing URIDownloader and register it in your factory,
like this:
final URIDownloader myDownloader = new URIDownloader()
{
@Override
public InputStream fetch(final URI source)
{
// your code here
}
};
// Example of registering a new scheme and unregistering another one
final JsonSchemaFactory factory = new JsonSchemaFactory.Builder()
.registerScheme("myscheme", myDownloader)
.unregisterScheme("ftp")
.build();
The validation process is a three step process:
The core package for the validation process is org.eel.kitchen.jsonschema.validator, and in particular JsonValidatorCache. This class is
instantiated by a schema factory and uses a LoadingCache to store already computed validators.
This is a critically important part of the validation process. You may encounter, during validation, what is called a JSON Reference. A JSON Reference is a JSON Object with one member named $ref, and the value of this member is a text value which embodies a URI. Implementations are required to follow JSON References until an actual final content is reached (and this content may, or may not, be a valid JSON Schema).
Now, more food for thought. Consider this schema:
{
"$ref": "some://where/else",
"minimum": 3
}
This is still a JSON Reference. Other schema keywords, such as minimum in this example, should be ignored. And this is what this implementation does.
This is an equally important part of the validation process. One thing to note about the previous step is that it will only check that JSON Reference resolution ultimately leads to a JSON document, whatever that document is. Which means it may not even be a JSON Object, therefore not a JSON Schema. This basic check is done at that level.
The syntax validation process implies a SyntaxValidator instance, which checks that
all keywords are syntactically correct using a series of SyntaxChecker objects.
The role of SyntaxChecker objects
in particular is very important: for a given keyword, a syntax checker ensures
that the value of this keyword is well formed. This is what allows the next step
in the process to be easier to implement.
This is the part which actually matters to end users: ensure that their data is valid.
The process begins with an instance of InstanceValidator. It will query, via the
factory, a set of KeywordValidator
instances. All of these keyword validators will then be called in turn to
validate the data.
There is one more step to validation. The step above will only have validated that the current "path" in the JSON instance will have been validated. However, in the event where the instance is a so-called container instance, that is it has children nodes, which is the case of objects and array, then a further validation step is issued so that all children get validated.
The two classes responsible for this, ArrayValidator and ObjectValidator, take responsibility
from this point and will restart the validation process from scratch for each of
the instance children.
It should be noted that while array instance children only have to match exactly one schema, object instance children may have to match several of them, which does nothing to simplify matters...
Those four schema keywords have the unfortunate property that they can also restart the validation process from their own:
Those four schema keywords are an implementor's worst nightmare. Remember that the validation process starts from $ref resolution: each of these keywords, should they have schema dependencies of some sort, have to start the whole validation process from scratch.
Copyright © 2012. All Rights Reserved.