Jackson – Serialize and Deserialize Dates

By default, Jackson serializes the Dates in numeric format. Learn to customize the serialization and deserialization of date and time types in Java using Jackson.

1. Setup

Add the latest version of Jackson, if you have already added it to the project.

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.13.3</version>
</dependency>

For demo purposes, we will use the following Message class.

@JsonInclude(JsonInclude.Include.NON_NULL)
class Message {

  private Long id;
  private String text;
  private Date date;
  private LocalDateTime localDateTime;

  //constructors, getters, setters
}

2. Default Serialization and Deserialization

The default serialization and deserialization of java.util.Date and other date-time types such as java.time.LocalDateTime is done in numeric formats. The Date is converted to the long type and other types are converted to string with number values.

To include support for new Java 8 Date Time classes, do not forget to include the JavaTimeModule to the JsonMapper or ObjectMapper.

Let us see the serialization with an example.

@Test
void testDefaultSerialization_ThenSuccess() throws JsonProcessingException {

  Message message = new Message(1L, "test");
  message.setDate(new Date(1661518473905L));
  message.setLocalDateTime(LocalDateTime.of(2022,8,30,14,16,20,0));

  JsonMapper jsonMapper = new JsonMapper();
  jsonMapper.registerModule(new JavaTimeModule());
  String json = jsonMapper.writeValueAsString(message);

  Assertions.assertEquals("{\"id\":1,\"text\":\"test\"," +
      "\"date\":1661518473905,\"localDateTime\":[2022,8,30,14,16,20]}", json);
}

Also, check out the deserialization from JSON to POJO as follows:

@Test
void testDefaultDeserialization_ThenSuccess() throws JsonProcessingException {

  String json = "{\"id\":1,\"text\":\"test\",\"date\":1661518473905," +
      "\"localDateTime\":[2022,8,30,14,16,20]}";

  JsonMapper jsonMapper = new JsonMapper();
  jsonMapper.registerModule(new JavaTimeModule());
  Message message = jsonMapper.readValue(json, Message.class);

  Assertions.assertEquals(1661518473905L, message.getDate().getTime());
  Assertions.assertEquals(LocalDateTime.of(2022,8,30,14,16,20,0), message.getLocalDateTime());
}

3. Using Custom Date Time Patterns

3.1. Serialization

For serializing the date time types using custom patterns, we can set the SimpleDateFormat to JsonMapper.

jsonMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

Now we can use this JsonMapper instance to write supported types using the configured custom pattern.

@Test
void testCustomFormat_ThenSuccess() throws JsonProcessingException {

  Message message = new Message(1L, "test");
  message.setDate(new Date(1661518473905L));
  message.setLocalDateTime(LocalDateTime.of(2022, 1, 1, 15, 30));

  JsonMapper jsonMapper = new JsonMapper();
  jsonMapper.registerModule(new JavaTimeModule());
  jsonMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

  String json = jsonMapper.writeValueAsString(message);

  Assertions.assertEquals("{\"id\":1,\"text\":\"test\"," +
      "\"date\":\"2022-08-26 18:24:33\"," +
      "\"localDateTime\":\"2022-01-01T15:30:00\"}", json);
}

Note that not all Java types support all custom patterns. For example, in the above code, if we use the LocalDate class, it will not give the desired result because there is no time information associated with the LocalDate class.

3.2. Deserialization

The deserialization process is very similar to serialization. It also uses the custom pattern configured using the SimpleDateFormat class.

@Test
void testCustomDateFormatDeserialization_ThenSuccess() throws JsonProcessingException {

  String json = "{\"id\":1,\"text\":\"test\",\"date\":1661518473905," +
      "\"localDateTime\":[2022,8,30,14,16,20]}";

  JsonMapper jsonMapper = new JsonMapper();
  jsonMapper.registerModule(new JavaTimeModule());
  jsonMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

  Message message = jsonMapper.readValue(json, Message.class);

  Assertions.assertEquals(1661518473905L, message.getDate().getTime());
  Assertions.assertEquals("2022-08-30T14:16:20", message.getLocalDateTime().toString());
}

4. Custom Serializer and Deserializer

If we want more control over how the JSON to POJO (or vice-versa) conversions happen, we can write custom classes and register them with Jackson.

4.1. Custom Serializer

The following is an example of a simple custom date serializer for LocalDateTime type. It extends StdSerializer class and override its serialize() method.

We can apply any custom pattern using the Java DateTimeFormatter class. We can customize it based on our complex requirements.

class CustomLocalDateTimeSerializer
    extends StdSerializer<LocalDateTime> {

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

  public CustomLocalDateTimeSerializer() {
    this(null);
  }

  public CustomLocalDateTimeSerializer(Class<LocalDateTime> t) {
    super(t);
  }

  @Override
  public void serialize(
      LocalDateTime value,
      JsonGenerator gen,
      SerializerProvider arg2)
      throws IOException, JsonProcessingException {

    gen.writeString(formatter.format(value));
  }
}

Note that we are not using Jackson’s support to process LocalDateTime class, so we can skip adding the JavaTimeModule module. Instead, we can add a SimpleModule after registering CustomLocalDateTimeSerializer with it.

@Test
void testCustomSerializer_ThenSuccess() throws JsonProcessingException {
  Message message = new Message(1L, "test");
  message.setLocalDateTime(LocalDateTime.of(2022, 1, 1, 15, 30));

  JsonMapper jsonMapper = new JsonMapper();

  SimpleModule module = new SimpleModule();
  module.addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer());
  jsonMapper.registerModule(module);

  String json = jsonMapper.writeValueAsString(message);

  Assertions.assertEquals("{\"id\":1,\"text\":\"test\"," +
      "\"localDateTime\":\"2022-01-01 15:30:00\"}", json);
}

4.2. Custom Deserializer

Adding a custom date deserializer is similar to what we saw in the custom serializer. It extends StdDeserializer class.

class CustomLocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {

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

  public CustomLocalDateTimeDeserializer() {
    this(null);
  }

  public CustomLocalDateTimeDeserializer(Class<?> vc) {
    super(vc);
  }

  @Override
  public LocalDateTime deserialize(JsonParser jsonparser,
                                   DeserializationContext context)
      throws IOException {
    String date = jsonparser.getText();
    return LocalDateTime.parse(date, formatter);
  }
}

Let us test the custom deserialization using a simple example.

@Test
void testCustomLocalDateTimeDeserializer_ThenSuccess() throws JsonProcessingException {

  String json = "{\"id\":1,\"text\":\"test\",\"localDateTime\":\"1986-04-08 12:30\"}";

  JsonMapper jsonMapper = new JsonMapper();

  SimpleModule module = new SimpleModule();
  module.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer());
  jsonMapper.registerModule(module);

  Message message = jsonMapper.readValue(json, Message.class);
  Assertions.assertEquals("1986-04-08T12:30", message.getLocalDateTime().toString());
}

5. Conclusion

In this Jackson tutorial, we learned the default serialization and deserialization of Java Date and Time type classes. We also learned to use the custom patterns using the SimpleDateFormat, and custom serializer and deserializer implementations.

Happy Learning !!

Sourcecode on Github

Comments

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode