Java 8 date time JSON formatting with Jackson

Upasana | October 24, 2019 | 3 min read | 5,493 views


In this tutorial we will learn how to format java 8 date time types (Instant, LocalDate, LocalDateTime, Date) to JSON using Jackson and Spring Boot 2.

Gradle dependencies

First of all we need to create a new Spring Boot 2.2.0 based project. The below gradle file will manage the dependencies for setting up Spring MVC.

build.gradle
plugins {
	id 'org.springframework.boot' version '2.2.0.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.shunya'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
}

Creating a POJO

We will create a simple POJO that holds different types of types i.e. java.util.Date, LocalDate, LocalDateTime, Instant.

import com.fasterxml.jackson.annotation.JsonFormat;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;

public class SampleDto {

    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "Asia/Kolkata")
    private Instant instant;

    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
    private Date date;

    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate localDate;

    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
    private LocalDateTime localDateTime;

    //Getters and Setter omitted for brevity
}

We will use this POJO for Jackson based JSON serialization/deserialization.

It is important to note here that we need to specify the timezone while dealing with java.time.Instant otherwise we will encounter the below exception:

nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unsupported field: YearOfEra

Controller Layer

We will create a controller that exposes two endpoints - one with HTTP POST for SampleDto another for HTTP GET for SampleDto.

DateController.java
@RestController
public class DateController {

    @PostMapping("/date")
    public SampleDto create(@RequestBody SampleDto payload) {
        return payload;
    }

    @GetMapping("/date")
    public SampleDto get() {
        final SampleDto dto = new SampleDto();
        final Instant time = Instant.ofEpochMilli(1571884105000L);
        dto.setInstant(time);
        dto.setDate(new Date(time.toEpochMilli()));
        dto.setLocalDate(time.atZone(ZoneId.of("UTC")).toLocalDate());
        dto.setLocalDateTime(time.atZone(ZoneId.of("UTC")).toLocalDateTime());
        return dto;
    }
}

JUNIT 5 test at Controller layer for JSR-310 types

Spring Boot 2.2.0 supports JUNIT5 out of the box without any custom configuration. All you need to do is to include the below dependency in build.gradle

build.gradle
dependencies {
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

test {
	useJUnitPlatform()
}

Here we will just test the controller layer using JUNIT 5 based tests in Spring Boot.

DateControllerTest.java
@ExtendWith(SpringExtension.class)
@WebMvcTest(DateController.class)
class DateControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void postDate() throws Exception {
        //language=JSON
        String jsonPayload = "{\n" +
                "  \"instant\": \"2019-10-24T07:58:25.000+05:30\",\n" +
                "  \"date\": \"2019-10-24T02:28:25.000+0000\",\n" +
                "  \"localDate\": \"2019-10-25\",\n" +
                "  \"localDateTime\": \"2019-10-24T02:28:25\"\n" +
                "}";
        this.mockMvc.perform(MockMvcRequestBuilders
                .post("/date")
                .content(jsonPayload)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andDo(print()).andExpect(status().isOk())
                .andExpect(content().string(CoreMatchers.notNullValue()));
    }
}

WebMvcTest annotation just bootstraps the provided controller instead of loading the entire application context.

Spring DateTimeFormat vs Jackson JsonFormat

@JsonFormat is a Jackson annotation (com.fasterxml.jackson.annotation), while @DateTimeFormat is a Spring annotation.

@JsonFormat controls the formatting during JSON serialization/deserialization of java Date time classes (JSR-310 java.time types - LocalDate/LocalDateTime/Instant/Date, etc)

@DateTimeFormat on the other hand controls formatting of a bean in Spring when it is rendered ina view.

Github Repository

You can grab the github repository for this project from this link: https://github.com/cancerian0684/tutorial-jsonformat-spring-boot


Top articles in this category:
  1. SendGrid Attachments with Spring Boot
  2. Generate UPI QR Code in Java
  3. Elasticsearch with Spring Boot + Spring Data
  4. Table backed global counter in spring hibernate
  5. Sendgrid Dynamic Templates with Spring Boot
  6. Spring Data ElasticSearch with Basic Auth
  7. Dialoglfow fulfillment with Spring Boot

Recommended books for interview preparation:

Find more on this topic: