Creating a custom serializer/deserializer in Java

3 minute read

dist files

In this post, we are going to see how we can serialize objects using JSON in the context of a Java REST API with a library called Jackson.

Why do we need serialization?

Serializers convert a Java object into a stream of bytes which can be persisted into a filesystem or shared between two different servers through a network connection.

Deserializers allow us to do the opposite process, translating from a stream of bytes that has been easy to transfer into the original Java Object.

With this process, we can transfer data more efficiently rather than transferring the original structure of the data object.

Serialization example

In the following example, we can see how serialization works. Given a class Employee

@JsonPropertyOrder({ "email", "name", "id", "yearsOfExperience" })
public class Employee {
    private long id;
    private String name;
    private String email;
    private int yearsOfExperience;
}

serializing an object will output a JSON with the following structure:

{
    "email":"contact@alexmanrique.com",
    "name":"Alex",
    "id":1,
    "yearsOfExperience":"10"
}

This is the default serialization that we got for the class Employee but what if we have a field that is not possible to serialize? Here is when we need to create a custom serializer for this field.

Java libraries to serialize and deserialize

There are some Java libraries out there, that you can use for serialization purposes. I choosed Jackson some time ago because this library its shipped with JBoss application server and I can use it using the provided scope when importing it using Maven.

Jackson is a mature JSON serialization/deserialization library that is built into all JAX-RS implementations and has extensive annotation support, however, there are other alternatives out there.

Lib name URL Github stars Forks
Jackson github.com/FasterXML/jackson 6K 1k
GSON github.com/google/gson 18,7K 3,6k
Fastjson github.com/alibaba/fastjson 22,5K 6k
Moshi github.com/square/moshi 6,7K 536
Jsoniter github.com/json-iterator/java 1,3K 424

According to the numbers of stars and forks done Fastjson from Alibaba is the library that right now has more support in the open-source community.

Using Jackson in our Maven Java application

To be able to use Jackson we need to import in your pom.xml file if we are using Maven:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>[2.6.0]</version>
</dependency>
<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jackson-provider</artifactId>
    <version>[2.3.10.Final]</version>
</dependency>

Java code example to serialize and deserialize

In the following example, we can see the class Employee that is using a serializer and deserializer for the attribute creationDate. We use the annotation @JsonDeserialize and @JsonSerialize to bind the usage of the serializer and deserializer to the attribute.

import org.codehaus.jackson.map.annotate.JsonSerialize;

import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;

@ApiModel(value = "Class that defines a Employee", subTypes = {Developer.class, ProductManager.class, MarketingExecutive.class})
public class Employee {

    private long id;
    @NotNull
    private String name;
    @NotNull
    private String email;
    @NotNull
    private int yearsOfExperience;
    @NotNull
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    private LocalDateTime creationDate;
    
    public Employee(){
        //needed for jackson serialization purposes
    }
    ...
}

Note that you need a default no args-constructor in your bean class otherwise we will receive an error similar to this.

org.codehaus.jackson.map.JsonMappingException: No suitable constructor found for type [simple type, class Employee]: can not instantiate from JSON object (need to add/enable type information?) at [Source: employee.json; line: 1, column: 2] at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163) at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObjectUsingNonDefault(BeanDeserializer.java:746) at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:683) at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580) at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732) at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1817)

In the following code we can see a LocalDateTimeSerializer where we serialize a LocalDateTime using the pattern "yyyy-MM-dd HH:mm:ss"

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator,
                          SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(localDateTime.format(formatter));
    }
}

In the following code we do the opposite operation, from a string with the format "yyyy-MM-dd HH:mm:ss" we parse it into a LocalDateTime again


import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;

public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {

    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        return LocalDateTime.parse(jsonParser.getText(), formatter);
    }
}

Conclusion

In this post, we have seen how to create a serializer and a deserializer in Java using the Jackson library. We have seen also that there are alternatives out there for serialization purposes.

I won't give your address to anyone else, won't send you any spam, and you can unsubscribe at any time.
Disclaimer: Opinions are my own and not the views of my employer

Updated:

Comments