[JAVA] JSON validation with JSON schema

When you have data in JSON format, you may want to check if the data is written according to predetermined rules. A standard called JSON Schema has been established for this purpose.

What is JSON Schema?

As an example, consider the following JSON-formatted data.

person.json


{
    "name": "Yamada Taro",
    "age": 42,
    "hobbies": ["baseball", "judo"]
}

In JSON Schema, such JSON-formatted data to be validated is called a JSON instance. Here, we want to set the following restrictions on the above JSON instance that represents "person".

Such a constraint is expressed in JSON Schema as follows. The JSON schema itself is also written in JSON format.

json:person.schema.json


{
    "type": "object",
    "properties": {
        "name": {
            "type": "string"
        },
        "age": {
            "type": "integer",
            "minimum": 0
        },
        "hobbies": {
            "type": "array",
            "items": {
                "type": "string"
            }
        }
    },
    "required": ["name"]
}

The JSON Schema specification is developed by json-schema-org. The latest specification at the time of writing this article is Draft-07.

Try validating JSON

Now let's actually validate the JSON instance according to the JSON schema. The following code is written in Java.

In this article, we will use Justify as the library for JSON validation. This library uses the interface of the javax.json package provided by Java API for JSON Processing (JSR 374) as it is for loading JSON instances. For this reason, API users do not need to be very aware that they are validating.

Preparation

Add the following two files as dependent files to the Java application to be validated. This is an example when Maven is used as a build tool.

pom.xml


<!--Library body-->
<dependency>
  <groupId>org.leadpony.justify</groupId>
  <artifactId>justify</artifactId>
  <version>2.0.0</version>
</dependency>

<!--Implementation of Java API for JSON Processing-->
<dependency>
  <groupId>org.glassfish</groupId>
  <artifactId>jakarta.json</artifactId>
  <classifier>module</classifier>
  <version>1.1.6</version>
</dependency>

The second dependency file is an example of using the Reference Implementation as the implementation of Java API for JSON Processing (JSR 374). You can safely replace it with another compatible implementation such as Apache Johnzon.

Validation with streaming API

There are two types of APIs in Java API for JSON Processing (JSR 374): streaming API and object model API. First, let's validate the JSON instance using the streaming API.

In the streaming API, control is returned to the caller in units of small events that occur during JSON loading, such as the beginning of an object, property name, property value, and so on. Use the javax.json.stream.JsonParser interface to load the JSON instance.

import java.nio.file.Paths;
import javax.json.stream.JsonParser;
import org.leadpony.justify.api.JsonSchema;
import org.leadpony.justify.api.JsonValidationService;
import org.leadpony.justify.api.ProblemHandler;

//Create a validation service.
JsonValidationService service = JsonValidationService.newInstance();
//Read the JSON schema from a file.
JsonSchema schema = service.readSchema(Paths.get("person.schema.json"));
//A handler that handles issues found during validation.
//Here, only output to standard output.
ProblemHandler handler = service.createProblemPrinter(System.out::println);

//Create a parser to read the JSON instance.
try (JsonParser parser = service.createParser(Paths.get("person.json"), schema, handler)) {
    //Handle all parser events
    while (parser.hasNext()) {
        //Parser event that occurred
        JsonParser.Event event = parser.next();
        //Handle events.
        doSomethingUseful(event);
    }
}

As a test, try entering an invalid JSON instance as follows.

person-bad.json


{
    "age": -1,
    "hobbies": ["baseball", "judo"]
}

Problems found during validation are output as follows:

[2 lines,13 rows]The number must be greater than or equal to 0.
[4 lines,1 row]Object is a property"name"Must have.

If the runtime locale is Japanese, the error message will also be Japanese.

Validation with object model API

Next, let's look at a code example using the object model API.

In the object model API, reading JSON builds the entire JSON object or array in memory. Use the javax.json.JsonReader interface to load the JSON instance.

import java.nio.file.Paths;
import javax.json.JsonObject;
import javax.json.JsonReader;
import org.leadpony.justify.api.JsonSchema;
import org.leadpony.justify.api.JsonValidationService;
import org.leadpony.justify.api.ProblemHandler;

//Create a validation service.
JsonValidationService service = JsonValidationService.newInstance();
//Read the JSON schema from a file.
JsonSchema schema = service.readSchema(Paths.get("person.schema.json"));
//A handler that handles issues found during validation.
//Here, only output to standard output.
ProblemHandler handler = service.createProblemPrinter(System.out::println);

//Create a reader to read the JSON instance.
try (JsonReader reader = service.createReader(Paths.get("person.json"), schema, handler)) {
    //Read JSON object
    JsonObject object = reader.readObject();
    //Process the read JSON object.
    doSomethingUseful(object);
}

Even in the case of the object model API, when a problem is found in the JSON instance, an error message similar to the streaming API is output.

Validation with binding API

Java has Java API for JSON Binding (JSR 367) as an API specification for directly converting a JSON instance to a POJO (Plain Old Java Object). Finally, let's validate the JSON instance using this API.

Since we are using Yasson as an implementation of Java API for JSON Binding (JSR 367), add this as a dependent file.

pom.xml


<!--Implementation of Java API for JSON Binding-->
<dependency>
  <groupId>org.eclipse</groupId>
  <artifactId>yasson</artifactId>
  <version>1.0.2</version>
</dependency>

Next, define the Person class as the destination POJO.

public class Person {
    public String name;
    public int age;
    public List<String> hobbies;
}

The validation code looks like this:

import java.nio.file.Paths;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.spi.JsonProvider;
import org.leadpony.justify.api.JsonSchema;
import org.leadpony.justify.api.JsonValidationService;

//Create a validation service.
JsonValidationService service = JsonValidationService.newInstance();
//Read the JSON schema from a file.
JsonSchema schema = service.readSchema(Paths.get("person.schema.json"));
//Create an instance of JsonProvider.
JsonProvider provider = service.createJsonProvider(
        schema, 
        parser->service.createProblemPrinter(System.out::println));
//Create an instance of Jsonb.
Jsonb jsonb = JsonbBuilder.newBuilder().withProvider(provider).build();

//Open a JSON instance as an input stream.
try (InputStream stream = Files.newInputStream(Paths.get("person.json"))) {
    //Convert a JSON instance to a Person object.
    Person person = jsonb.fromJson(stream, Person.class);
    //Process the Person object.
    doSomethingUseful(person);
}

Similar to the code introduced so far, if a problem is found in the JSON instance you entered, an error message will be printed.

Other validator implementations

For JSON validators based on the JSON Schema specification, the latest list is provided in Implementations on json-schema.org, including for programming languages other than Java. The Justify introduced in this article was developed by myself.

Recommended Posts

JSON validation with JSON schema
Diffed with JSON
Import JSON with SolrJ
Handle JSON with minimal-json
Self-made Validation with Spring
Format JSON with org.json
Process validation messages with Decorator
[Java] JSON communication with jackson
Get validation results with Spring Boot
Add Bean Validation with Micronaut (Java)
Easy input check with Bean Validation!
Handle JSON effortlessly with Alamofire's respone Json
Apply regular expression matching with JSON Sassert
Create your own validator with Bean Validation
How to implement TextInputLayout with validation function
Working with huge JSON in Java Lambda
Form class validation test with Spring Boot
Convert JSON to TSV and TSV to JSON with Ruby