P_M
P_M

Reputation: 2942

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();
    }
}

Upvotes: 6

Views: 6434

Answers (3)

kj007
kj007

Reputation: 6254

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/");
    }
}

Upvotes: 7

mrkernelpanic
mrkernelpanic

Reputation: 4451

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

Upvotes: 3

Nick
Nick

Reputation: 3951

Create object mapper manually

@Bean
@Primary
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();

    JavaTimeModule timeModule = new JavaTimeModule();
    mapper.registerModule(timeModule);

    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    mapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);

    return mapper;
}

Upvotes: 1

Related Questions