Java XML and JSON: Document processing for Java SE, Part 2: JSON-B

Bind Java objects to JSON documents with the Java API for JSON Binding

data conversion
erhui1979 / Getty Images

In this post, we'll continue exploring XML and JSON in Java 11 and beyond.

Examples in this post will introduce you to JSON-B, the JSON Binding API for Java. After a quick overview and installation instruction, I'll show you how to use JSON-B to serialize and deserialize Java objects, arrays, and collections; how to customize serialization and deserialization using JSON-B; and how to use JSON-B adapters to convert source objects to target objects during serialization or deserialization.

The material for this post is completely new, but could be considered an additional chapter (Chapter 13) for my new book, recently published by Apress: Java XML and JSON, Second Edition.

What is JSON-B?

JSON-B is a standard binding layer and API for converting Java objects to and from JSON documents. It's similar to Java Architecture for XML Binding (JAXB), which is used to convert Java objects to and from XML.

JSON-B is built on top of JSON-P, the JSON Processing API used for parsing, generating, querying, and transforming JSON documents. JSON-B was introduced by Java Specification Request (JSR) 367 more than a year after the final release of JSR 353, the JSR for JSON-P.

The JSON-B API

The Java API for JSON Binding (JSON-B) website introduces JSON-B and provides access to various resources, including API documentation. According to the documentation, the JSON-B module stores six packages:

  • javax.json.bind: Defines the entry point for binding Java objects to JSON documents.
  • javax.json.bind.adapter: Defines adapter-related classes.
  • javax.json.bind.annotation: Defines annotations for customizing the mapping between Java program elements and JSON documents.
  • javax.json.bind.config: Defines strategies and policies for customizing the mapping between Java program elements and JSON documents.
  • javax.json.bind.serializer: Defines interfaces for creating custom serializers and deserializers.
  • javax.json.bind.spi: Defines a Service Provider Interface (SPI) for plugging in custom JsonbBuilders.

The JSON-B website also provides a link to Yasson, a Java framework that provides a standard binding layer between Java classes and JSON documents, and an official reference implementation of the JSON Binding API.

Download and install JSON-B

JSON-B 1.0 is the current version at the time of writing. You can obtain the Yasson reference implementation of this library from the Maven repository. You will need to download the following JAR files:

  • Javax JSON Bind API 1.0: Contains all of the JSON-B classfiles. I downloaded javax.json.bind-api-1.0.jar.
  • Yasson: Contains the Eclipse-based reference implementation of JSON-B. I downloaded yasson-1.0.3.jar.
  • JSR 374 (JSON Processing) Default Provider: Contains all of the JSON-P 1.0 classfiles along with the Glassfish default provider classfiles. I downloaded javax.json-1.1.4.jar.

Add these JAR files to your classpath when compiling and running code that uses these libraries:

javac -cp javax.json.bind-api-1.0.jar;. main source file
java -cp javax.json.bind-api-1.0.jar;yasson-1.0.3.jar;javax.json-1.1.4.jar;. main classfile

Serializing and deserializing Java objects with JSON-B

The javax.json.bind package provides the Jsonb and JsonbBuilder interfaces, which serve as the entrypoint to this library:

  • Jsonb provides overloaded toJson() methods for serializing trees of Java objects to JSON documents, and fromJson() methods for deserializing JSON documents to trees of Java objects.
  • JsonbBuilder provides newBuilder() and other methods for obtaining a new builder, and build() and create() methods for returning new Jsonb objects.

The following code example demonstrates the basic usage of the Jsonb and JsonBuilder types:

// Create a new Jsonb instance using the default JsonbBuilder implementation.
Jsonb jsonb = JsonbBuilder.create();

// Create an Employee object from a hypothetical Employee class.
Employee employee = ...

// Convert the Employee object to a JSON document stored in a string.
String jsonEmployee = jsonb.toJson(employee);

// Convert the previously-created JSON document to an Employee object.
Employee employee2 = jsonb.fromJson(jsonEmployee, Employee.class);

This example invokes Jsonb's String toJson(Object object) method to serialize a Java object, (Employee). This method is passed the root of the Java object tree to serialize. If null is passed, toJson() throws java.lang.NullPointerException. It throws javax.json.bind.JsonbException when an unexpected problem (such as an I/O error) occurs during serialization.

This code fragment also invokes Jsonb's <T> T fromJson(String str, Class<T> type) generic method, which is used for deserialization. This method is passed the string-based JSON document to deserialize and the type of the resulting Java object tree's root object, which is returned. This method throws NullPointerException when null is passed to either parameter; it throws JsonbException when an unexpected problem occurs during deserialization.

I excerpted the code fragment from a JSONBDemo application that provides a basic demonstration of JSON-B. Listing 1 presents the source code for this demo.

Listing 1. JSONBDemo.java (version 1)

import java.time.LocalDate;

import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;

public class JSONBDemo
{
   public static void main(String[] args)
   {
      Jsonb jsonb = JsonbBuilder.create();
      Employee employee = new Employee("John", "Doe", 123456789, false,
                                       LocalDate.of(1980, 12, 23),
                                       LocalDate.of(2002, 8, 14));
      String jsonEmployee = jsonb.toJson(employee);
      System.out.println(jsonEmployee);
      System.out.println();
      Employee employee2 = jsonb.fromJson(jsonEmployee, Employee.class);
      System.out.println(employee2);
   }
}

main() first creates a Jsonb object followed by an Employee object. It then calls toJson() to serialize the Employee object to a JSON document that's stored in a string. After printing this document, main() invokes fromJson() with the previous string and Employee's java.lang.Class object to deserialize the JSON document to another Employee object, which is subsequently printed.

Listing 2 presents Employee's source code.

Listing 2. Employee.java (version 1)

import java.time.LocalDate;

public class Employee
{
   private String firstName;

   private String lastName;

   private int ssn;

   private boolean isMarried;

   private LocalDate birthDate;

   private LocalDate hireDate;

   private StringBuffer sb = new StringBuffer();

   public Employee() {}

   public Employee(String firstName, String lastName, int ssn, boolean isMarried,
                   LocalDate birthDate, LocalDate hireDate)
   {
      this.firstName = firstName;
      this.lastName = lastName;
      this.ssn = ssn;
      this.isMarried = isMarried;
      this.birthDate = birthDate;
      this.hireDate = hireDate;
   }

   public String getFirstName()
   {
      return firstName;
   }

   public String getLastName()
   {
      return lastName;
   }

   public int getSSN()
   {
      return ssn;
   }

   public boolean isMarried()
   {
      return isMarried;
   }

   public LocalDate getBirthDate()
   {
      return birthDate;
   }

   public LocalDate getHireDate()
   {
      return hireDate;
   }

   public void setFirstName(String firstName)
   {
      this.firstName = firstName;
   }

   public void setLastName(String lastName)
   {
      this.lastName = lastName;
   }

   public void setSSN(int ssn)
   {
      this.ssn = ssn;
   }

   public void setIsMarried(boolean isMarried)
   {
      this.isMarried = isMarried;
   }

   public void setBirthDate(LocalDate birthDate)
   {
      this.birthDate = birthDate;
   }

   public void setHireDate(LocalDate hireDate)
   {
      this.hireDate = hireDate;
   }

   @Override
   public String toString()
   {
      sb.setLength(0);
      sb.append("First name [");
      sb.append(firstName);
      sb.append("], Last name [");
      sb.append(lastName);
      sb.append("], SSN [");
      sb.append(ssn);
      sb.append("], Married [");
      sb.append(isMarried);
      sb.append("], Birthdate [");
      sb.append(birthDate);
      sb.append("], Hiredate [");
      sb.append(hireDate);
      sb.append("]");
      return sb.toString();
   }
}

Compile Listings 1 and 2 as follows:

javac -cp javax.json.bind-api-1.0.jar;. JSONBDemo.java

Run the application as follows:

java -cp javax.json.bind-api-1.0.jar;yasson-1.0.3.jar;javax.json-1.1.4.jar;. JSONBDemo

You should observe the following output (spread across multiple lines for readability):

{"SSN":123456789,"birthDate":"1980-12-23","firstName":"John","hireDate":"2002-08-14",
 "lastName":"Doe","married":false}

First name [John], Last name [Doe], SSN [123456789], Married [false],
 Birthdate [1980-12-23], Hiredate [2002-08-14] 

Rules for working with JSON-B

While playing with this application, I observed some interesting behaviors that led me to formulate the following rules concerning Employee:

  • The class must be public; otherwise, an exception is thrown.
  • toJson() will not serialize fields with non-public getter methods.
  • fromJson() will not deserialize fields with non-public setter methods.
  • fromJson() throws JsonbException in the absence of a public noargument constructor.

In order to seamlessly convert between Java object fields and JSON data, JSON-B has to support various Java types. For example, JSON-B supports the following basic Java types:

  • java.lang.Boolean
  • java.lang.Byte
  • java.lang.Character
  • java.lang.Double
  • java.lang.Float
  • java.lang.Integer
  • java.lang.Long
  • java.lang.Short
  • java.lang.String

Additional types such as java.math.BigInteger, java.util.Date, and java.time.LocalDate are supported. Check out the JSON-B specification for a complete list of supported types.

Serializing and deserializing arrays and collections with JSON-B

The previous section focused on serializing and deserializing single Java objects. JSON-B also supports the ability to serialize and deserialize object arrays and collections. Listing 3 provides a demonstration.

Listing 3. JSONBDemo.java (version 2)

import java.time.LocalDate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;

public class JSONBDemo
{
   public static void main(String[] args)
   {
      arrayDemo();
      listDemo();
   }

   // Serialize and deserialize an array of Employee objects.
   static void arrayDemo()
   {
      Jsonb jsonb = JsonbBuilder.create();
      Employee[] employees =
      {
         new Employee("John", "Doe", 123456789, false,
                      LocalDate.of(1980, 12, 23),
                      LocalDate.of(2002, 8, 14)),
         new Employee("Jane", "Smith", 987654321, true,
                      LocalDate.of(1982, 6, 13),
                      LocalDate.of(2001, 2, 9))
      };
      String jsonEmployees = jsonb.toJson(employees);
      System.out.println(jsonEmployees);
      System.out.println();
      employees = null;
      employees = jsonb.fromJson(jsonEmployees, Employee[].class);
      for (Employee employee: employees)
      {
         System.out.println(employee);
         System.out.println();
      }
   }

   // Serialize and deserialize a List of Employee objects.
   static void listDemo()
   {
      Jsonb jsonb = JsonbBuilder.create();
      List<Employee> employees =
         Arrays.asList(new Employee("John", "Doe", 123456789, false,
                                    LocalDate.of(1980, 12, 23),
                                    LocalDate.of(2002, 8, 14)),
                       new Employee("Jane", "Smith", 987654321, true,
                                    LocalDate.of(1982, 6, 13),
                                    LocalDate.of(1999, 7, 20)));
      String jsonEmployees = jsonb.toJson(employees);
      System.out.println(jsonEmployees);
      System.out.println();
      employees = null;
      employees = jsonb.fromJson(jsonEmployees,
                                 new ArrayList<>(){}.
                                     getClass().getGenericSuperclass());
      System.out.println(employees);
   }
}

Listing 3 is a simple extension of Listing 1, and uses the same Employee class presented in Listing 2. Additionally, this code example calls the same toJson() and fromJson() methods.

1 2 Page 1
Page 1 of 2