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.
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".
name
property is a stringhobbies
property is an array, and the element type is a string.name
property cannot be omittedSuch 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.
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.
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.
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.
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.
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.
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