It has been about 1 month now since I started writing REST APIs for Nuxeo. We use Google AutoValue and Jackson for data transfer objects (DTO). Today, I would like share some feedback with you.
What is AutoValue?
Value classes are extremely common in Java projects. These are classes for which you want to treat any two instances with suitably equal field values as interchangeable. AutoValue provides an easier way to create immutable value classes, with a lot less code and less room for error, while not restricting your freedom to code almost any aspect of your class exactly the way you want it.
The class using AutoValue and Jackson looks like:
Note that we only define the abstract classes — the concrete classes are generated by AutoValue using Annotation Processor:
AutoValue_Company.class
AutoValue_Company.Builder.class
The naming convention for AutoValue is having prefix “AutoValue_“, and continue with the class name of the abstract class:
AutoValue_<ClassName>.class
If you want to know more about annotation processor, take a look at OpenJDK: Compilation Overview.
Jackson
Jackson annotations help Jackson to understand how to serialize and deserialize the value class.
Action | Description |
---|---|
Serialization | Java → JSON using value class annotations |
Deserialization | JSON → Java using builder class annotations |
When using Jackson in JAX-RS, a Jackson JSON provider needs to be registered as singleton in the REST application:
You might need a more complex Jackson JSON provider to fit your business logic, see section Jackson Advanced Configuration.
AutoValue Advanced Configuration
In the following sections, we’ll talk about advanced configuration: ensure the solution “AutoValue + Jackson” fits your application requirements.
Optional Value
Some fields might be optional in your AutoValue object. Ordinarily the
generated constructor will reject any null
values. If you want to accept
null
, you can either add a @Nullable
annotation (see AutoValue: how to
use nullable properties?) or use Optional<T>
. My preferred one is
Optional<T>
:
Note that on the getter side (1), there is only one getter method, which return
optional. On the other side, for setter, there are two setter methods (2)(3),
allowing you to provide a description directly or a wrapped description. For
Jackson, you must annotate the builder method with Optional<T>
as parameter,
otherwise AutoValue reject the null
case provided by Jackson.
Jackson Advanced Configuration
This section describes how to customize your Jackson JSON provider. Here’s a template for bootstrapping the customization, but of course you can do it in other ways too:
Enable Java 8 Support
Register the following modules into your ObjectMapper
to enable to Java 8
supports, including Java Time:
These modules require the following dependencies:
com.fasterxml.jackson.module:jackson-module-parameter-names
com.fasterxml.jackson.datatype:jackson-datatype-jdk8
com.fasterxml.jackson.datatype:jackson-datatype-jsr310
See GitHub project FasterXML/jackson-modules-java8 for more configuration detail.
Enable ISO-8601 For DateTime Serialization
If you want the datetime fields to be serialized as ISO-8601, you need to
explicitly set the date format as StdDateFormat
for standard serializers and
deserializers. Therefore, for serialization it defaults to using an ISO-8601
compliant format (format String yyyy-MM-dd'T'HH:mm:ss.SSSZ
)
and for deserialization, both ISO-8601 and RFC-1123. You also need to disable
the serialization feature WRITE_DATES_AS_TIMESTAMPS, which serializes the date
time to timestamp. The final code block:
Preserve JSON Timezone After Deserialization
By default, mapper drops timezone during deserialization. It adjust dates to context’s (web application’s) timezone. If you want to preserve user’s timezone, you can do the following:
This is useful when you’ve users coming from different timezones, e.g. from France and China, and you don’t want to modify the datetime created by users. However, Jackson is not the only part to take care — you need to ensure the entire stack of your application supports timezone, including database.