Standards

Vert.x Json Schema

Preview

Vert.x Json Schema provides an extendable and asynchronous implementation for Json Schema specification. You can use Json Schemas to validate every json structure. This module provides:

  • Implementation of draft 2020-12

  • Implementation of draft 2019-09

  • Implementation of draft 7

  • Implementation of draft 4

  • Dereferencing of $ref resolution and caching

  • DSL to build schemas programmatically

Using Vert.x Json Schema

To use Vert.x Json Schema, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-json-schema</artifactId>
 <version>4.5.11</version>
</dependency>
  • Gradle (in your build.gradle file):

dependencies {
 compile 'io.vertx:vertx-json-schema:4.5.11'
}

Concepts

JsonSchema

Schemas can exist in 2 flavours:

  • JSON as in JSON notation

  • Boolean as in true/false

The JsonSchema interface allows both types to be handled without the constant check of the underlying type.

SchemaRepository

The SchemaRepository holds JsonSchema instances. It performs dereferencing of schemas to speed up validation. The repository is a simple key store, this means that it does not allow duplicate ids.

The repository can then create Validator instances aware of all sub schemas in the repository.

Validator

As the name implies the Validator validates an object using a start schema. The output format is dependent of the configuration.

Parse a schema

When working with multiple schemas or sub-schemas, it is recommended to use a Repository.

To parse a schema you first need a JsonSchema and some initial configuration. Since schemas can contain references it is required for the validator and repository to be aware of your application baseUri. This allows you to reference your own schemas in other sub-schemas. For the purpose of dereferencing, you don’t need to configure a draft.

SchemaRepository repository =
  SchemaRepository.create(new JsonSchemaOptions().setBaseUri("https://vertx.io"));

You can use JsonSchema instances for different Validator and you can parse different JsonSchema with JsonParser directly.

Now you can parse the schema:

JsonSchema schema = JsonSchema.of(object);

// Or

repository.dereference(JsonSchema.of(object));

Remember that for security reasons, this module will not attempt to download any referenced sub-schema. All required sub-schemas should be provided to a repository object.

Validate

Given the dynamic nature of json-schema and the conditional if-then-else it is not possible to validate in a streaming scenario. Validation is for this reason a blocking operation. If you are aware that validation will be a very expensive process, then it is advisable to run the validation on a dedicated thread pool or using executeBlocking. A schema could have two states:

To validate a schema:

OutputUnit result = Validator.create(
    schema,
    new JsonSchemaOptions().setDraft(Draft.DRAFT7))
  .validate(json);

if (result.getValid()) {
  // Successful validation
}

Defining a custom JSON format

By default, the schema validator will perform an NOOP on unknown formats, so they will be treated as valid inputs. It may be the case that additional format checking is required depending on the JSON specification you decide to use. If you need to define additional format checking, you can supply your own implementation of JsonFormatValidator when creating a SchemaRepository or Validator:

JsonFormatValidator customFormatValidator = (instanceType, format, instance) -> {
  if ("string".equals(instanceType) && "allUpercase".equals(format)) {
    if (instance.toString().equals(instance.toString().toUpperCase())) {
      return null;
    }
    return String.format("String does not match the format \"%s\"", format);
  }
  return null;
};

SchemaRepository repository = SchemaRepository.create(new JsonSchemaOptions(), customFormatValidator);

JsonSchema schema = JsonSchema.of(Schemas.stringSchema().toJson());
Validator validator = Validator.create(schema, new JsonSchemaOptions(), customFormatValidator);

Building your schemas from code

If you want to build schemas from code, you can use the included DSL. Only Draft-7 is supported for this feature.

To start, add static imports for Schemas and Keywords

Creating the schema

Inside Schemas there are static methods to create the schema:

SchemaBuilder intSchemaBuilder = intSchema();
SchemaBuilder objectSchemaBuilder = objectSchema();

Using the keywords

For every schema you can add keywords built with Keywords methods, depending on the type of the schema:

stringSchema()
  .with(format(StringFormat.DATETIME));
arraySchema()
  .with(maxItems(10));
schema() // Generic schema that accepts both arrays and integers
  .with(type(SchemaType.ARRAY, SchemaType.INTEGER));

Defining the schema structure

Depending on the schema you create, you can define a structure.

To create an object schema with some properties schemas and additional properties schema:

objectSchema()
  .requiredProperty("name", stringSchema())
  .requiredProperty("age", intSchema())
  .additionalProperties(stringSchema());

To create an array schema:

arraySchema()
  .items(stringSchema());

To create a tuple schema:

tupleSchema()
  .item(stringSchema()) // First item
  .item(intSchema()) // Second item
  .item(booleanSchema()); // Third item

$ref and aliases

To add a $ref schema you can use the Schemas.ref method. To assign an $id keyword to a schema, use id

You can also refer to schemas defined with this dsl using aliases. You can use alias to assign an alias to a schema. Then you can refer to a schema with an alias using Schemas.refToAlias:

intSchema()
  .alias("myInt");

objectSchema()
  .requiredProperty("anInteger", refToAlias("myInt"));

Using the schema

After you defined the schema, you can call toJson to return the JSON notation of the schema:

JsonObject schema = objectSchema()
  .requiredProperty("name", stringSchema())
  .requiredProperty("age", intSchema())
  .additionalProperties(stringSchema())
  .toJson();