Customize your Java-JSON serialization using Jackson Annotations

Marcos Abel
Trabe
Published in
4 min readMar 4, 2019
Photo by Fancycrave on Unsplash

One of the tasks you are almost guaranteed to face in every Java project is serializing objects to JSON and deserializing JSON back to Java objects. Jackson is the de facto standard library for this task in the Java ecosystem and it is also the default (de)serializer for Spring MVC and Spring Boot.

In most scenarios, Jackson just works under the hood and does the right thing with the default configuration. But sometimes you just need more control over the (de)serialization process.

Default behavior

The first thing you should know about Jackson (or any other library you use) is how it behaves out of the box. With “out of the box” we mean “as configured out of the box in the context of a Spring Boot application”.

If you want to dive into the specifics of how exactly Spring Boot configures Jackson, you can read the sources, but for this story is enough to see how it works with a couple of examples.

Serialization

With the default configuration, Jackson will happily serialize a POJO like this one:

to the following JSON representation:

{
"fieldOne": "Some text",
"fieldTwo": "Some other text",
"someDate": 1551198824771
}

(NOTE: we are using Project Lombok to write our POJOs. If you are not familiar with it, you can read this post)

As you can see, Jackson serializes our Java object to JSON including all the “accessible fields” and uses an epoch to represent our Calendar.

Deserialization

With the default configuration, we can deserialize a JSON like this one with no problem:

{
"fieldOne": "Field One",
"fieldTwo": "Field Two",
"someDate": 1551198824881
}

Jackson is smart enough to build the POJO using the builder generated by Project Lombok. We could even provide a JSON like this one:

{
"fieldOne": "Field One",
"fieldTwo": "Field Two",
"someDate": 1551198824881,
"randomField": "Some random text"
}

and Jackson would discard the anrandomField and correctly deserialize the JSON to a Java object.

Customize the serializer

This default behavior is good enough for most scenarios, but sometimes we need some customization. We can use Jackson annotations to customize the serialization process.

Exclude fields

Jackson provides us with an annotation for removing fields from the serialization result: @JsonIgnore . We can add this annotation to the field fieldTwo:

The annotated field will be excluded from the generated JSON:

{
"fieldOne": "Some text",
"someDate": 1551198824771
}

Customize date formats

We can also customize the format of our date fields using the @JsonFormat annotation. If we want our date fields in ISO 8601 format we just need to annotate our field someDate with:

With this annotation in our POJO, the JSON output will be:

{
"fieldOne": "Some text",
"someDate": "2019-02-26T16:33:44.771+0000"
}

We can further customize the format of our dates including a specific pattern for the conversion:

With this annotation, the JSON would look like this:

{
"fieldOne": "Some text",
"someDate": "2019-02-26"
}

Specifying custom names for the fields

We can specify a custom name for a field using the annotation @JsonProperty:

With this annotation, the name of the annotated field will be date in the JSON representation:

{
"fieldOne": "Some text",
"date": "2019-02-26"
}

Snake Case? No problem

Sometimes we need to consume JSON files which were written using different conventions than the ones used in our own codebase. A typical case is consuming JSON with fields named using snake case.

We could annotate every field in the object with a @JsonProperty annotation with the snake case representation but this is a cumbersome and error-prone process. We have a better solution: the@JsonNaming annotation:

With this set of annotations, when we try to serialize our Java object we will get a JSON with the fields in snake case:

{
"field_one": "Some text",
"some_date": "2019-02-26T18:32:01.748+0000"
}

Of course, this works both ways: you can now also deserialize JSON in snake case.

Strategies for different naming needs

Jackson provides us with built-in naming strategies for most widespread JSON naming conventions. Let’s see the additional options we have and the way a field fieldOne would be represented with every one of them:

  • PropertyNamingStrategy.KebabCaseStrategy: concatenates the different parts of the name using hyphens. Example: field-one.
  • PropertyNamingStrategy.LowerCaseStrategy: concatenates the different parts in lowercase and without connectors. Example: fieldone.
  • PropertyNamingStrategy.UpperCamelCaseStrategy: concatenates the different parts of the name capitalizing every one of them. Example: FieldOne.

Customize enum representation

By default, Jackson will represent Java enums using a String containing the value of the enum.

Let’s create an enum for this example:

We can now add a field someEnum of type JacksonExampleEnum to our POJO:

An object of this class will be serialized as follows:

{
"field_one": "Some text",
"some_date": "2019-02-26T18:58:38.283+0000",
"some_enum": "OPTION_1"
}

We can annotate any method of the enum with @JsonValue and the result of that method will be the value used for the serialization. Let’s say we annotate the method getA():

The resulting JSON representation will be:

{
"field_one": "Some text",
"some_date": "2019-02-26T18:58:38.283+0000",
"some_enum": "0"
}

We can also annotate the enum with @JsonFormat(shape = JsonFormat.Shape.OBJECT):

Jackson will happily include the enum as an object in the resulting JSON representation:

{
"field_one": "Some text",
"some_date": "2019-02-26T19:18:27.538+0000",
"some_enum": {
"a": "0",
"b": "0"
}
}

Wrapping up

Jackson is a very customizable library. The default configuration provided by Jackson itself and Spring Boot is good enough for a variety of scenarios, but when we need total control over the serialization process we can use the power of Jackson annotations.

In this story, we have focused on the most frequent scenarios of annotation-based customization but Jackson supports further customization. A good starting point for those advanced tweaking possibilities can be found here.

--

--