Learn with our tutorials and training

developerWorks provides tutorials, articles and other
technical resources to help you grow your development skills
on a wide variety of topics and products. Learn about a specific
product or take a course and get certified. So, what do you want to learn
about?

Featured products

Featured destinations

Find a community and connect

Learn from the experts and share with other developers in one of our
dev centers. Ask questions and get answers with dW answers. Search for local events
in your area. All in developerWorks communities.

Is it time for a JSON binding standard?

Content series:

This content is part # of 4 in the series: Get started with the JSON Binding API, Part 4

https://www.ibm.com/developerworks/library/?series_title_by=**auto**

Stay tuned for additional content in this series.

This content is part of the series:Get started with the JSON Binding API, Part 4

Stay tuned for additional content in this series.

About this series: Java EE has long supported XML, but
built-in support for JSON data has been notably missing. Java EE 8
changes that, bringing powerful JSON binding features to the core Java
enterprise platform. Get started with JSON-B, and learn how it
combines with the JSON Processing API and other technologies for
manipulating JSON documents in Java enterprise applications.

JSON has been the data interchange format of choice for nearly a decade,
but until now we have not had a specification robust enough to standardize
on. With the release of the JSON Binding API, vendors have the opportunity
to standardize both default and custom binding operations in JSON parsers.
As developers, we should insist that they do.

Comparing serialization and deserialization behavior across three JSON
parsers brings to light the differences and idiosyncrasies in how these
tools handle normal binding operations. It also becomes clear that
numerous key features are inconsistently supported, with some tools
offering them while others do not.

While there is value in uniqueness, that should not come at the expense of
functionality. Allowing default behavior to differ so substantially
between implementations may influence the choice of tool more than
differences in performance, which should be the primary deciding
factor.

The case for a JSON binding standard

Building an effective standard takes time, and works best when it’s based
on established use cases and best practices. A good standard is the
product of domain knowledge based on years of experimentation, correction,
and innovation. In the case of JSON parsers, I believe the time is ripe
for a standard.

What do you think? I believe adopting a JSON binding
standard is the next evolutionary step for JSON, and that JSR 367 is
the right specification to do it. What do you think? I hope you’ll
share your thoughts by leaving a comment
below.

Standardizing JSON binding operations and features would enable developers
to compare tools based on the efficiency and performance of the underlying
engine. Implementers would be freed to focus more on performance than
features. The overall quality of tools would improve

Standardization does not stifle innovation; instead, it reflects the
innovation that has already occurred. It provides a sound base for future
innovation and advancements, and the best additions become candidates for
incorporation into the standard.

I believe the case is strong for standardizing, and with the JSON Binding
API specification there is no good reason not to do it. In the following
sections I will compare core features among three JSON binding tools.
While the comparisons are interesting, my main point is to show the
discrepancy in how the tools handle binding operations and type formats in
Java™.

Comparing JSON parsers

This article compares two popular frameworks—FastXML's Jackson Project and
Google's Gson—with the
JSON Binding API.

We’ll start by comparing the frameworks’ default treatment of key data
types in structured JSON documents. We’ll then look at how each framework
handles customization for those data types, ranging from basic Java types
to the new date and time formats found in Java 8.

You’ll see for yourself how each JSON framework behaves when serializing
and deserializing Java objects to and from JSON. My goal isn’t a
scientific review or benchmark comparison, but an overview of the
differences and consistencies between the frameworks. Ultimately, I am
most interested in demonstrating the benefits of standardizing on the JSON
Binding specification.

For the examples in this article I’ve used the JSON Binding API version 1.0
with JSON Processing 1.1 as the provider, Gson version 2.8.2, and Jackson
version 2.9.2. I’ve also created a GitHub repository containing code examples and unit tests so that
you can duplicate the examples for yourself.

Binding models

In a JSON parser, the binding model is the mechanism of
serialization and deserialization. All three of the frameworks have
default binding behavior for common binding scenarios, and each one offers
custom binding solutions. For custom binding, developers have the option
to use compile-time annotations or runtime configurations. In this section
I show how JSON-B, Jackson, and Gson approach custom binding through
runtime configuration. The models are quite different, so this is a good
starting point for comparison. Later sections will explore customization
features in detail.

Runtime binding with Jackson

Jackson’s runtime model differs from JSON-B’s in that you configure the
object mapper engine directly. This engine is then used to perform
serialization and deserialization on the target object. As you can see in
Listing 2, the ObjectMapper provides a substantial range of
configuration methods.

Runtime binding with Gson

Gson configures an instance of the GsonBuilder from which a
Gson instance is created. A Gson object provides methods for
operations of serialization and deserialization. You can see the Gson
runtime model in Listing 3.

Serialization with all three frameworks results in the JSON document shown
in Listing 5. This serialization behavior, as you will see, is currently
the only behavior that is consistent across frameworks. Note that fields
are self describing in order to make matching the JSON properties to the
source class easier.

All three of the JSON parsers use default configurations for operating on
the basic types, with few configuration options. (See Get started with the JSON Binding API, Part 3 for a demonstration
of number formatting with JSON-B.)

Specific Java types

So-called specific types include more complex JDK types
such as those that extend java.lang.Number. Among these are
BigDecimal, AtomicInteger, and
LongAdder; Optional types including
OptionalInt and Optional<type>; and the
URL and URI instances.

First let’s compare Number types. All JSON frameworks perform
serialization by simply extracting the internal number as the property
value. The treatment of BigDecimal, BigInteger,
AtomicInteger, and LongInteger is consistent
across the three frameworks.

The URI and URL classes are also treated the same
way across all three frameworks: after serialization, the internal value
is expressed as a String in the resulting JSON document.

Optional types are not consistently treated, however. Table 1
shows the results of serializing the two instances in Listing 6.

As you see, JSON-B fully supports Optional values by peeking
inside and retrieving the internal value. Neither Jackson or Gson support
Optionals, out-of-the-box, and the serialization of a
String Optional is the least consistent.

Gson serializes the structure of the Optional to a JSON object
containing the String, while Jackson does not even include
the value of the underlying instance; instead it only states that it is
present.

While OptionalInt and the rest of the Optional
number family are not well supported in Gson or Jackson, they are at least
serialized to a sensible JSON object that includes the underlying value
and the boolean representing that the value is present.

Date, time, and calendar types

Dates, times, and calendar type are numerous, including both the old
java.util.Date and java.util.Calendar types and
the new Java 8 date and time classes found in java.time.

Given the extensive range of types to compare I have chosen just three:

An instance of Calendar with the date set using the
builder.

An instance of Date with the date set from a
String and applying the SimpleDateFormat.

An instance Java 8's LocalDate with the date set using
the parse() method.

The Calendar type

The Calendar instance is configured with the date of December
25, 2017, as shown in Listing 7.

Listing 7. Calendar type

new Calendar.Builder().setDate(2017, 1, 25).build()

Table 2 shows the results of serializing this instance using each of the
three frameworks.

Table 2. Calendar instance serialized

It is clear from Table 2 that each framework implements a different
methodology to serialize a Calendar instance. JSON-B outputs
the date with a zero time and timezone. Jackson produces the number of
milliseconds since the epoch (January 1, 1970) and Gson produces a JSON
object containing the numerical parts of the date.

The difference in the default behavior is stark. While it’s possible to
configuration these formats to something more readable, there really
shouldn’t be such a range of difference in the output format.

The Date and LocalDate types

Now let’s see how Date and LocalDate are treated.
Listing 8 shows the code for creating these two instances and Table 3
shows their serialization.

Listing 8. LocalDate and Date instances

new SimpleDateFormat("dd/MM/yyyy").parse("25/12/2017");
LocalDate.parse("2017-12-25");

The Date type

JSON Binding ignores the original date format and applies the default
formatting style, adding time and timezone information. Jackson returns
the milliseconds since the epoch, and Gson applies the MEDIUM
date/time style.

The LocalDate type

A LocalDate instance is treated differently, and from the
perspective of JSON-B a little more logically than Date.
JSON-B outputs just the date in the default format, with no time or time
zone information. Jackson produces a JSON object by calling the accessor
methods in the instance, effectively treating LocalDate as a
POJO. Gson constructs an object with three properties for year, month, and
day.

Clearly the Date, LocalDate, and
Calendar instances are not treated consistently across the
three JSON frameworks.

Configuring date and time formats

There are both compile-time and runtime customizations for date and time
formats. Only JSON-B and Jackson provide annotations that specify a date
and time format for a given property, method, or class, as shown in
Listings 9 and 10.

Listing 9. Configuring the date format at compile time
with JSON Binding

@JsonbDateFormat(value = "MM/dd/yyyy", locale = "Locale.ENGLISH")

Listing 10. Configuring the date format at compile time
with Jackson

@JsonFormat(pattern = "MM/dd/yyyy", locale = "Locale.ENGLISH")

All three parsers have runtime configurations that set the format globally,
as shown in Listings 11, 12, and 13.

Listing 11. Configuring the date format at runtime with
JSON Binding

new JsonbConfig().withDateFormat("MM/dd/yyyy", Locale.ENGLISH)

Listing 12. Configuring the date format at runtime with
Jackson

new ObjectMapper().setDateFormat(new SimpleDateFormat("MM/dd/yyyy"))

Listing 13. Configuring the date format at runtime with
Gson

new GsonBuilder().setDateFormat("MM/dd/yyyy")

Arrays, collections, and maps

The parsers are surprisingly consistent in their treatment of arrays,
collections, and maps. The reason is that each of these structures maps
directly to its equivalent JSON type. So the code in Listing 14 serializes
to the JSON document in Listing 15 regardless of which JSON framework is
in use.

Listing 15. The serialized array, collection, and
map

Nulls, nulls in collections, and
Optional.empty()

Nulls are not serialized by JSON Binding or Gson, but they are preserved by
Jackson. On deserialization the absence of a value in a JSON document does
not result in a call to the corresponding setter method in the target
object. If the value is null, however, then it is set as a normal
value.

For all three frameworks nulls are preserved in arrays, maps, and
collections by default.

Optional.empty()

The Optional.empty() value represents a nonexistent value and
thus JSON Binding treats it similarly to null: the JSON document will not
include the property on serialization. Both Jackson and Gson will attempt
to serialize this value to a JSON object, however: Jackson treats
Optional.empty() as a POJO, while Gson produces an empty JSON
Object.

Table 4. Serialization of an Optional.Empty()
instance

JSON-B

Jackson

Gson

"Property not included in JSON document."

{
"emptyOptional": {
"present": false
}
}

{
"emptyOptional": {}
}

Configuration options for null

The JSON Binding API provides both runtime and compile-time configuration
options for nulls. For a compile-time configuration to include a null for
a specific field, you could use the @JsonbProperty annotation
and set the nillable flag to true.
Alternatively, you could globally set inclusion at the class or package
level with the @JsonbNillable annotation, as shown in Listing
16.

Listing 20. A Gson runtime configuration to exclude
nulls

Once again you see that type serialization is treated inconsistently across
the three frameworks. Not only are nulls treated inconsistently, but even
the configuration options differ.

Field visibility

A field’s visibility determines whether or not it will be serialized to the
JSON document and deserialized to the field of the target instance.
Additionally, each framework interprets visibility differently.

The code in Listing 21 was serialized using all three frameworks. The
fields are self describing. Some fields have corresponding setter/getter
methods (where the field name suggests this), while the remaining fields
do not. Additionally, one virtual field has no corresponding field and is
represented by only a getter method.

So that we can compare how each framework approaches field visibility,
Table 5 presents the outcomes of serializing the code in Listing 21 and
compiling the results.

Table 5. Summary of each framework’s default handling of
field visibility

Access Via...

Field Modifier

JSON-B

Jackson

Gson

Field

private

default

protected

public

final public

final private

static public

Public Getter

private

default

protected

public

final private

final public

static public

virtual field

The only consistency is that all three frameworks respect public non-static
access modifiers. Additionally, none of the frameworks ever serializes
public static fields, even if the field has a public getter method.

It is clear that Gson does not respect a field's access modifier at all and
includes all fields in the serialization, regardless of the modifier
specified (except public static fields). Gson also does not include
virtual fields, which both JSON-B and Jackson do.

JSON-B and Jackson are identical in their approach to field visibility.
Gson differs by including final private fields and not including virtual
fields.

Visibility configuration

Visibility configuration options are quite extensive and can be complex.
Let’s look at how each framework configures visibility.

JSON Binding

JSON-B provides a simple @JsonbTransient annotation that
excludes any annotated field from serialization and deserialization.

In JSON-B there is a more sophisticated way to control field visibility.
You can create a custom visibility strategy by implementing the
PropertyVisibilityStrategy interface and setting it as a
runtime property on the JsonbConfig instance. Listing 22
demonstrates. (For an example implementation of the
PropertyVisibilityStrategy class, see Part 3.)

Listing 22. Setting a custom visibility strategy in
JSON-B

Jackson

Jackson provides compile-time annotations that ignore any field annotated
with @JsonIgnore, along with any field specifically mentioned
in the list passed to the @JsonIgnoreProperties({"aField"})
class-level annotation. Method and field visibility can be configured with
the @JsonAutoDetect, as shown in Listing 23.

Listing 23. Setting field visibility with Jackson
annotations

Jackson also offers a runtime configuration that allows the explicit
inclusion of fields based on their access modifier. The code example in
Listing 24 shows the inclusion of public and protected fields during
serialization.

Listing 24. Configure public and protected fields for
inclusion

Gson

Gson provides a compile-time annotation for specifying fields to include in
serialization and deserialization operations. In this case, you mark each
field with the @Expose annotation and set the
serialize and/or deserialize flag to
true or false, as shown in Listing 25.

Listing 25. Using the @Expose annotation

In order to use this annotation you must enable it by calling the
excludeFieldsWithoutExposeAnnotation() method on the
GsonBuilder instance, as shown in Listing 26.

Listing 26. Enabling the @Expose annotation

new GsonBuilder().excludeFieldsWithoutExposeAnnotation();

Alternatively, fields can be explicitly excluded based on the fields access
modifier as shown in Listing 27.

Listing 27. Exclude fields based on its modifier

new GsonBuilder().excludeFieldsWithModifiers(Modifier.PROTECTED);

Configuring property order

The IETF RFC
7159 JSON interchange format specification defines a JSON object
as an "unordered collection of zero or more name/value pairs". In some
cases, however, it is required that properties be displayed in a given
order. By default, JSON-B orders properties lexicographically, while
Jackson and Gson use the order in which the fields appear in the
class.

In all three frameworks, you are given the capability to specify property
order by explicitly listing field names or by specifying an order
strategy.

JSON Binding

JSON-B provides both a runtime and compile-time mechanism for specifying
property order. The order is specified at runtime by passing a list of
fields to the @JsonbPropertyOrder annotation, as shown in
Listing 28.

Listing 28. Specifying field order explicitly in
JSON-B

@JsonbPropertyOrder({"firstName", "title", "author"})

For runtime configuration, you set a global order strategy
by specifying the desired order strategy on an instance of
JsonbConfig, as shown in Listing 29.

Listing 29. Specifying order reverse lexicographically in
JSON-B

new JsonbConfig()
.withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE);

Jackson

Jackson also provides a way to explicitly state the order of fields, as
well as identifying the order as alphabetic, as shown in Listing 30.

Conclusion

Gson and Jackson are established and very popular JSON frameworks that
offer an extensive range of features, especially for customization. As I
have demonstrated, however, there is very little consistency in how simple
scenarios are handled across the frameworks. Even the simplest matter of
null treatment differs.

Where there is consistency in the handling of types, it is because there
could be no other option. Basic types can only realistically be serialized
as their actual value, while maps and collections translate directly to
their JSON object equivalents, and vice versa. The most stark
inconsistency is the treatment of dates and times: Not one of the
frameworks serializes these types in the same way. Although there are
configuration options to control formats, the range of inconsistency
points to the need for a standard.

Developers shouldn’t be forced to compromise performance for features, and
a JSON binding standard would resolve that. I believe adopting a JSON
binding standard is the next evolutionary step for JSON, and that JSR 367
is the right specification to do it. What do you think? I hope you’ll
Please share your thoughts below.