How to parse JSON in Java

In today’s data-driven world, the ability to effectively parse and process JSON data has become an essential skill for Java developers. This tutorial will guide you through the fundamentals of parsing JSON data using the Jakarta JSON API framework which includes two main models: JSON Processing (JSON-P) and JSON Binding (JSON-B).

Binding and Processing JSON

Firstly, let’s clarify the distinction between the API which you can use to process JSON Data and the API that you can use to bind JSON Data to Java and viceversa:

  • JSON-P (JSON-Processing, JSR 374): Specifies a very low-level processing library which can be carried out with two models for JSON processing. The Object Model API and the Streaming API.
  • JSON-B (JSON-Binding, JSR 367): Provides a binding layer on top of JSON-P, making it easier to convert objects to and from JSON.

Let’s recap the differences with the following table:

FeatureJSON-P (JSR 374)JSON-B (JSR 367)
PurposeLow-level JSON processingHigh-level JSON-to-Java object binding
ComplexityMore manual effort and JSON parsing conceptsStreamlined and annotations-based
Use CasesDevelopers requiring precise JSON manipulationDevelopers simplifying JSON-to-Java object conversion
Mapping MechanismManual JSON parsing and creationAnnotations for object-to-JSON mapping

Getting started with JSON-P

JSON-P provides the following models for JSON processing:

  • The Object Model API which provides an easy way to work with JSON and holds all the data in memory.
  • The Streaming API which is a low-level API designed to process large amounts of JSON data efficiently.

Using the JSON Object Model API

The Object Model API is a high-level API that provides immutable object models for JSON object and array structures. These JSON structures are represented as object models via the Java types JsonObject and JsonArray.

The Object Model API is similar to the DOM API for XML and it is more flexible and enables processing that requires random access to the complete contents of the tree. However, it is often not as efficient as the streaming model and requires more memory.

JsonObjectBuilder jsonBuilder = Json.createObjectBuilder();
jsonBuilder.add("firstName", "John");
jsonBuilder.add("surName", "Smith");
jsonBuilder.add("city", "New york");
JsonObject json = jsonBuilder.build(); 

On the other hand, you can read a JSON document and parse each field reading JsonObject properties:

JsonReader jsonReader = Json.createReader(this.getClass().getClassLoader().getResourceAsStream("data.json"));
JsonObject obj = jsonReader.readObject();
writer.write("Name " +obj.getString("firstName"));
writer.write("<br/>Surname " +obj.getString("lastName"));

A JsonObject in JSON-P is immutable, unlike with JSON-B. If you want to update fields, you must create a new JsonObject, duplicate the fields, and then make changes.

Using the JSON Streaming Model API

The streaming API is a low-level API designed to process large amounts of JSON data efficiently. This model is adequate for local processing where only specific parts of the JSON structure need to be accessed, and random access to other parts of the data is not required.

The streaming API is similar to the StAX API for XML and consists of the interfaces JsonParser for consuming JSON and JsonGenerator for producing JSON.

Here is an example:

JsonGenerator generator = Json.createGenerator(System.out);

generator.writeStartArray().writeStartObject().write("name", "John")
        .write("surname", "Doe").writeEnd().writeStartObject()
        .write("name", "Anna").write("surname", "Smith").writeEnd()
        .writeEnd();

In order to parse JSON with Streaming API, you can use the JsonParser API whch contains methods to parse JSON data using the streaming model. JsonParser provides forward, read-only access to JSON data using the pull parsing programming model:

JsonParser parser = Json.createParser(this.getClass()
        .getClassLoader().getResourceAsStream("data.json"));

while (parser.hasNext()) {
    JsonParser.Event event = parser.next();

    switch (event) {
        case START_ARRAY:
            writer.write(event.toString() + "\n");
            break;
        case END_ARRAY:
            writer.write(event.toString() + "\n");
            break;
        case START_OBJECT:
            writer.write("Found JSON object \n");
            break;
        case END_OBJECT:
            writer.write("\nEnd JSON object"+ "\n");
            break;
        case VALUE_FALSE:
        case VALUE_NULL:
        case VALUE_TRUE:
        case KEY_NAME:
            writer.write(event.toString() + " " + parser.getString()
                    + " - ");
            break;
        case VALUE_STRING:
            writer.write(event.toString() + " " + parser.getString()
                    + " - ");
            break;
        case VALUE_NUMBER:
            writer.write(event.toString() + " " + parser.getString());
            break;
    }
}

} catch (IOException e) {
e.printStackTrace();
}

In order to build JSON-P applications, you need this dependency available:

<dependency>
    <groupId>jakarta.json</groupId>
    <artifactId>jakarta.json-api</artifactId>
    <scope>provided</scope>
</dependency>

Getting started with JSON-B

JSON-B, which is a standard binding layer for converting Java objects to/from JSON messages, included as part of JSR 367 Specification. For this reason, you would typically create a model Layer for handling the conversion between the JSON and the Java layer. The typical use case is a REST application which creates (POST) or retrieves (GET) data in JSON format.

Here is an example:

@Path("/jsonb")
public class JsonService {

    @POST
    @Path("/tojava")
    public Response createJava(@FormParam("json") String json) {
        Response response;

        Jsonb jsonb = JsonbBuilder.create();

        Person p = jsonb.fromJson(json, Person.class);
        System.out.println("Created Person " + p);
        response = Response.ok(p.toString()).build();

        return response;
    }

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/tojson")
    public Response createJson(@FormParam("name") String name, @FormParam("surname") String surname, @FormParam("address") String address, @FormParam("city") String city) {
        Response response;
        Person p = new Person(name, surname, address, city);

        Jsonb jsonb = JsonbBuilder.create();
        String jsonString = jsonb.toJson(p);
        response = Response.ok(jsonString).build();

        return response;
    }
}

So in this example, JSON is converted into/from the Java Bean named Person. Please be aware that everything is strongly typed with JSON-B. If you get a field name wrong, for example, you would see a compile failure since there wouldn’t be such a method.

In order to build JSON-B applications, you need this dependency available:

<dependency>
        <groupId>jakarta.json.bind</groupId>
        <artifactId>jakarta.json.bind-api</artifactId>
        <scope>provided</scope>
</dependency>

This tutorial was just a brief introduction to JSON Processing in Java. You can read more details in the following tutorials:

How to use JSON-P to create and parse JSON in Java

How to use JSON-B for parsing Java objects to/from JSON