JSON-B: A Java API for JSON Binding
When JSON-B and JSON-P are combined, all of the tools are put in place to process and work with the JSON data format in Java.
Join the DZone community and get the full member experience.
Join For FreeJavaScript Object Notation, or JSON, is a lightweight text-based data format that is widely used in data exchange between web services and network-based applications.
JSON supports two standard data structures, object and arrays, to serialize and deserialize data and information. For instance, an object “Person” with name, lastName, and age fields and a collection (array) of “Person” objects, “Personnel,” would be constructed in JSON format as follows.
JSON Object
{
“name”:”sam”,
“lastName” : “sepassi”,
“age” : 33
}
JSON Array
{
" Personnel ":
[
{" name ":"name1", "lastName":"lastName1",”age”:20},
{" name ":"name2", "lastName":"lastName2",”age”:21},
{" name ":"name3", "lastName":"lastName3",”age”:22}
]
}
JSON supports the combination of two structures. By combining object and array data structures, the following structure is constructed in JSON format:
{
"name": "sam",
"lastName": "sepassi",
"age": 33,
"contactNumbers": [{
"type": "cell",
"number": "+1***********"
}, {
"type": "office",
"number": "+1***********"
}]
}
Generating and parsing JSON is supported in Java EE 7 as JSR 353, a Java API for JSON processing JSON-P. JSON-P provides APIs to parse, generate, and transform Java objects to and from textual JSON representations. Using JSON-P, the previous JSON structure can be generated using the following code:
JsonObject model = Json.createObjectBuilder()
.add("name", "sam")
.add("lastName", "sepassi")
.add("age", 33)
.add("contactNumbers", Json.createArrayBuilder()
.add(Json.createObjectBuilder()
.add("type", "cell")
.add("number", "+1***********"))
.add(Json.createObjectBuilder()
.add("type", "office")
.add("number", "+1***********")))
.build();
By outputting model.toString(), the following JSON structure (which is identical to the previous JSON structure) is created:
{"name":"sam","lastName":"sepassi","age":33,
"contactNumbers":[{"type":"cell","number":"+1***********"},
{"type":"office","number":"+1***********"}]}
To reverse this operation using JSON-P, the following code will create an instance of JsonObject again:
JsonReader jsonReader = Json.createReader(new StringReader(model.toString()));
JsonObject jsonObject = jsonReader.readObject();
Having the jsonObject is handy because any processing can be done on object fields. For instance, to retrieve the value for the “name” field, the get(Object key) method can be called from the JsonObject:
jsonObject.get("name");
One thing that is missing in the JSON-P specification is an object binding API to support direct object mapping to and from JSON representations. This is the same idea as the JAXB (Java Architecture for XML Binding) when considering the XML data format in Java. JAXB is a collection of APIs that are used to bind XML schemas to java objects and vice versa. For instance, in JAXB, the annotations API (javax.xml.bind.annotation) is used to bind Java elements to XML schemas.
In the absence of such APIs, there are other solutions such as Gson by Google, Genson, and Jackson. These alternate solutions provide APIs to do binding between JSON data and Java objects but there has never been a standard Java specification to support such a feature.
Recently, a new specification called JSON-B (Java API for JSON Binding) released a new public draft for public review and comment. JSON-B is a complement to JSON-P, providing the missing API for directly binding java objects to JSON data and vice versa. JSON-B is planned to be part of JAVA EE 8 in the near future. Similar to JAXB, JSON-B provides a high-level API for JSON and object binding.
From this point on, some important sections of this specification will be introduced. The specification details and API may change in the near future until the final specification is available.
Mapping Objects
Let’s consider the “Person” Java object:
public class Person {
public String Name;
public String lastName;
public int age;
public String getName() {
return Name;
}
public void setName(String Name) {
this.Name = Name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
In JSON-B, the gateway to object-JSON serialization and deserialization is the Jsonb object. The Jsonb object can be created from the JsonbBuilderclass.
Jsonb jsonb = JsonbBuilder.create();
To serialize the “Person” Java object to JSON representation using Jsonb, the toJson(Object object)method is used:
String JsonRepresentation = jsonb.toJson(person);
By outputting the JsonRepresentationstring value, the following JSON string is presented:
{"Name":"Sam","lastName":"Sepassi","age":33}
Jsonb can be used again to deserialize the JSON structure back into a Java object. For instance, to convert the JSON data from the previous step to a “Person” Java object using a Jsonb object, the fromJson(String JSON,Class<T> type) method is used:
Person newPerson = jsonb
.fromJson("{\"Name\":\"Sam\",\"lastName\":\"Sepassi\",\"age\":33}", Person.class);
To support such straightforward functionality and be able to seamlessly convert between object fields and JSON data, JSON-B has to support various Java types. To support different Java types for the serialization and deserialization of various field types, such as String and Integer, the specification specifies a list of Java classes and types to be supported by JSON-B.
The important types supported by JSON-B are listed below. For more information and details about the types supported by JSONB, please refer to the JSON-B specification available here.
Basic Java Types
- java.lang.String
- java.lang.Character
- java.lang.Byte
- java.lang.Short
- java.lang.Integer
- java.lang.Long
- java.lang.Float
- java.lang.Double
- java.lang.Boolean
Specific Standard Java SE Types
- java.math.BigInteger
- java.math.BigDecimal
- java.net.URL
- java.net.URI
- java.util.Optional
- java.util.OptionalInt
- java.util.OptionalLong
- java.util.OptionalDouble
Dates
- java.util.Date
- java.util.Calendar
- java.util.GregorianCalendar
- Java.util.TimeZone
- java.util.SimpleTimeZone
- java.time.Instant
- java.time.LocalDate
- java.time.LocalTime
- java.time.LocalDateTime
- java.time.ZonedDateTime
- java.time.OffsetDateTime
- java.time.OffsetTime
- java.time.ZoneId NormalizedZoneId
- java.time.ZoneOffset NormalizedZoneId
- java.time.Duration ISO 8601
- java.time.Period
Mapping Arrays and Collections
Besides supporting single object conversion, JSON-B supports the following collection interfaces, classes and corresponding implementations to do the conversion between Java collections and JSON array data structures:
Collectionsinterfaces, Classes, and Implementations
- java.util.Collection
- java.util.Map
- java.util.Set
- java.util.HashSet
- java.util.NavigableSet
- java.util.SortedSet
- java.util.TreeSet
- java.util.LinkedHashSet
- java.util.TreeHashSet
- java.util.HashMap
- java.util.NavigableMap
- java.util.SortedMap
- java.util.TreeMap
- java.util.LinkedHashMap
- java.util.TreeHashMap
- java.util.List
- java.util.ArrayList
- java.util.LinkedList
- java.util.Deque
- java.util.ArrayDeque
- java.util.Queue
- java.util.PriorityQueue
- java.util.EnumSet
- java.util.EnumMap
To demonstrate the collection serialization using JSON-B, Lets create two “Person” objects:
Person First = new Person();
First.setName("Sam");
First.setLastName("Sepassi");
First.setAge(33);
Person Second = new Person();
Second.setName("Tom");
Second.setLastName("Sepassi");
Second.setAge(35);
Then, add these two objects to an ArrayList of type “Person”:
List<Person> Personnel = new ArrayList<>();
Personnel.add(First);
Personnel.add(Second);
As before, the toJson(Object object) method from Jsonb will do the conversion:
Jsonb jsonb = JsonbBuilder.create();
String result = jsonb.toJson(Personnel);
The result is the JSON array representation of the “Personnel” object:
[
{"Name":"Sam","age":33,"lastName":"Sepassi","name":"Sam"},
{"Name":"Tom","age":35,"lastName":"Sepassi","name":"Tom"}
]
To do the deserialization from a JSON array structure:
Personnel= jsonb.fromJson(result, new ArrayList (){}.getClass());
Customized Mapping
As with any standard functionality, there is always a need to do customization on functionalities and behaviors. JSON-B supports both compile-time customization by Annotations (javax.json.bind.annotation) and runtime customizations by JsonbConfig and JsonbBuilder classes.
Another interesting concept in customized mapping is the introduction of Adapters, which will be introduced shortly.
Compile Time Customization
Let’s take look at how annotations could be used to customize the default behavior of JSON-B with three simple examples. Consider the conversion of Person java object to JSON which had been demonstrated before. In this conversion, if we want a field such as Age to be excluded from the JSON representation, the JsonbTransient annotation could be used on the corresponding class field.
@JsonbTransient
public int age;
To customize a field’s name in JSON representation, the JsonbProperty annotation could be used on the field itself or the corresponding getter method.
@JsonbProperty("firstName")
public String Name;
@JsonbProperty("firstName")
public String getName() {
return Name;
}
To specify the order in which the fields will appear in the JSON structure, the JsonbPropertyOrder annotation could be used.
@JsonbPropertyOrder(LEXICOGRAPHICAL / ANY / REVERSE)
public class Person {
}
There are other annotations as well. The following list is the annotations that are supported by JSON-B:
JsonbCreator
JsonbDateFormat
JsonbNillable
JsonbNumberFormat
JsonbProperty
JsonbPropertyOrder
JsonbTransient
JsonbTypeAdapter
JsonbTypeDeserializer
JsonbTypeSerializer
JsonbValue
JsonbVisibility
To read more about JSOB-B annotations and their functionalities, please refer to the JSON-B specification or API documentation.
Runtime Customization
To do the customization during runtime, the JsonbConfig and JsonbBuilder classes could be utilized. The JsonbConfig class offers various methods for programmatic customizations.
JsonbConfig config = new JsonbConfig()
.withFormatting(…)
.withNullValues(…)
.withEncoding(…)
.withStrictIJSON(…)
.withPropertyNamingStrategy(…)
.withPropertyOrderStrategy(…)
.withPropertyVisibilityStrategy(…)
.withAdapters(…)
.withBinaryDataStrategy(…);
To create a customized JSON data using the runtime customization feature, instantiate the JsonbConfig object, set the required properties, and finally use it to create a Jsonb object. For instance, to output the JSON result in a more formatted fashion rather than a single line JSON string, a JsonbConfig object could be created with the formatting property set to true (by calling the withFormatting(Boolean.TRUE)) as illustrated bellow:
JsonbConfig config = new JsonbConfig().withFormatting(Boolean.TRUE);
Jsonb jsonb = JsonbBuilder.create(config);
Person person = new Person();
person.setName("Sam");
person.setLastName("Sepassi");
person.setAge(33);
System.out.println(jsonb.toJson(person));
The result will be a formatted JSON representation:
{
"Name":"Sam",
"age":33,
"lastName":"Sepassi"
}
Adapters
Last but not least, another interesting concept in the JSON-B specification is the introduction of Adapters. Adapters can be used to transform field values between serialization and deserialization phases. JSON-B specification defines the Adapters as “custom code to convert the 'unmappable' type into another one that JSON-B can handle.”
Adapters mostly cover logical- and application-level data conversion concepts. Consider application level encryption and decryption where the JSON data model has to be encrypted but the JAVA model stays intact or cases where the JSON representation having additional fields (control fields for instance) comparing to JAVA object having only data from the database tables. This feature is a useful tool to be used when considering special cases of data exchange between subsystems.
Adapters consists of three main components:
1. The Original Object
This is the Java object that is going to be modified (or to be adapted to some predefined rules) by an adapter.
2. Adapted Object
This is the transformed object containing modified or additional fields that are transformed by the adapters.
3. The Adapter
This is an instance of JsonbAdapter.class that is the class used by JSON-B to do transformation between the serialization and deserialization phases.
To demonstrate adapters, consider a use-case in which a financial subsystem has to present the income value of a person "before 30% tax reduction" to the outside world but has to persist the income value "after 30% tax reduction" internally.
To demonstrate the income scenario, an income field is added to the “Person” class.
public String Name;
public String lastName;
public int age;
public int income;
Then, an AdaptedPerson Java class is created with the fields as the “Person” Java class the same as the adapted Java object. The MeAdapter.class is created to do the adaptation between the data serialization and deserialization phases as the Adapter class.
public class MeAdapter implements JsonbAdapter<Person, AdapterPerson> {
@Override
public AdapterPerson adaptToJson(Person orgnl) throws Exception {
AdapterPerson adapterPerson = new AdapterPerson();
adapterPerson.setName(orgnl.getName());
adapterPerson.setLastName(orgnl.getLastName());
adapterPerson.setAge(orgnl.getAge());
adapterPerson.setIncome((int) (orgnl.getIncome() * 1.3));
return adapterPerson;
}
@Override
public Person adaptFromJson(AdaptedPerson adptd) throws Exception {
Person person = new Person();
person.setName(adptd.getName());
person.setLastName(adptd.getLastName());
person.setAge(adptd.getAge());
person.setIncome((int) (adptd.getIncome() / 1.3));
return person;
}
}
Using the internal value of income, 30% tax is added to the value during JSON serialization and it is subtracted after deserialization.
To tell the JSON-B to use the adapter, the programmatic customization using JsonbConfig should be used again. The rest is exactly the same as before.
JsonbConfig config = new JsonbConfig()
.withAdapters(new MeAdapter())
.withFormatting(Boolean.TRUE);
Jsonb jsonb = JsonbBuilder.create(config);
Person person = new Person();
person.setName("Sam");
person.setLastName("Sepassi");
person.setAge(33);
person.setIncome(1000);
String JSON = jsonb.toJson(person);
{
"name":"Sam",
"age":33,
"income":1300,
"lastName":"Sepassi"
}
person = jsonb.toJson(JSON,Person.class);
person.getIncome(); // 1000
Conclusion
JSON-B features are complements to the available JSON processing features in Java EE 7 known as JSON-P. Combining JSON-B and JSON-P, all the tools are in place to process and work with the JSON data format in Java.
Opinions expressed by DZone contributors are their own.
Comments