Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot 2.04 Jackson cannot serialize LocalDateTime to String

I have Spring boot application and LocalDateTime properties serialized as JSON array, instead of String.

As I found while search on web, all I need is to set up in application.properties

spring.jackson.serialization.write-dates-as-timestamps=false

I also tried to put jackson-datatype-jsr310 to dependencies, but no luck either

Also I tried to add annotation:

@DateTimeFormat(iso = ISO.DATE_TIME)

it not helps too.

I saw a lot of people get in something similar, but their solutions seems related to Spring Boot 1.x, while I use 2.04

Also I use Lombok, but not sure is it affects serialization format.

How can I track down and fix the date serialization format to be ISO date String?

Here response example (start is LocalDateTime and I want to have it as ISO String):

{
  "id": 3,
  "enabled": true,
  "outletId": 5,
  "reason": "hello",
  "start": [
    2019,
    9,
    10,
    10,
    42,
    11
  ],
  "status": "AVAILABLE"
}

Here the REST controller method response object:

@Data
@Entity
@Table(indexes = { @Index(columnList = ("outletId"),name="outlet_id_index"), 
        @Index(columnList = ("start"),name="start_index"),
        @Index(columnList = ("outletId, start"),name="outlet_id_start_index")})
public class OutletChron extends BaseEntity {
    private Long outletId;
    private String reason;

    @DateTimeFormat(iso = ISO.DATE_TIME)
    private LocalDateTime start;
    @Enumerated(EnumType.STRING)
    @Column(length = 30)
    private OutletStatus status;
}

Here my POM:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.banquets</groupId>
    <artifactId>Banquet</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>Banquet</name>
    <description></description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web-services</artifactId> 
            </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> 
            </dependency> -->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

UPDATE

I build a new project just to test, and I found there String format was default for LocalDateTime mapping. I was able to track down that format changes once I configure swagger. So without this swagger config I have String format:

@Configuration
@EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {

    @Autowired
    ServletContext servletContext;

    @Bean
    public Docket productApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.demo"))
                .build();
    }

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

UPDATE this Swagger config seems works (Date format is String and I can access Swagger UI at http://localhost:8000/api/swagger-ui.html

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket apiDocket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build();
    }
}
like image 891
Pavlo Morozov Avatar asked Sep 11 '18 13:09

Pavlo Morozov


People also ask

How do I serialize JSON in Spring Boot?

1. Overview When using JSON format, Spring Boot will use an ObjectMapper instance to serialize responses and deserialize requests. In this tutorial, we'll take a look at the most common ways to configure the serialization and deserialization options.

What is the default date serialization format in Jackson?

It's important to note that Jackson will serialize the Date to a timestamp format by default (number of milliseconds since January 1st, 1970, UTC). The actual output of the “ event ” serialization is: 3. Serialize Date to ISO-8601 Serializing to this terse timestamp format is not optimal.

How to use localdate over date in Spring Boot?

LocalDate came with Java 8 and is part of the new standard API in Java for working with dates. However, if you want to effectively use LocalDate over Date in a Spring Boot application, you need to take some extra care, since not all tools support LocalDate by default, yet. Spring Boot includes the popular Jackson library as JSON (de-)serializer.

How to serialize date to timestamp in Java 8?

We'll start by serializing a simple java.util. Date, then Joda-Time, and finally, the Java 8 DateTime. 2. Serialize Date to Timestamp First, let's see how to serialize a simple java.util.Date with Jackson. In the following example, we'll serialize an instance of “ Event, ” which has the Date field “ eventDate “:


2 Answers

Spring boot 2.x does not required to import JSR310 specifications as now its part of spring framework now so no need to import them and string formatting will automatically work.

If you need to override some default configuration of spring boot then you need to implement WebMvcConfigurer instead of extending WebMvcConfigurationSupport.

In your case, if you want to put static files somewhere else not in default resources folder then you might need to override addResourceHandlers and register paths.

If resources in default path then not required and just removing extending WebMvcConfigurationSupport will work for default string formatting.

UPDATED ANSWER:

If you use WebMvcConfigurationSupport that means uts an indication that it shouldn't auto-configure Spring MVC, means default settings will not work and you have to define all things here by overriding its support methods. So instead of WebMvcConfigurationSupport implement WebMvcConfigurer instead.

Here is the updated config.

@Configuration
@EnableSwagger2
public class SwaggerConfig implements WebMvcConfigurer {

    @Bean
    public Docket productApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.demo"))
                .build();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}
like image 74
kj007 Avatar answered Nov 13 '22 11:11

kj007


For me the following works:

@JsonFormat(pattern = "dd.MM.yyyy HH:mm")
private LocalDateTime startTime;

This will print the date in a string format e.g. like 11.09.2018 15:44

like image 43
mrkernelpanic Avatar answered Nov 13 '22 13:11

mrkernelpanic