JSON Java 8 LocalDateTime format in Spring Boot
Asked Answered
E

17

154

I'm having a small problem with formatting a Java 8 LocalDateTime in my Spring Boot Application. With 'normal' dates I have no problem, but the LocalDateTime fields are converted to the following:

"startDate" : {
    "year" : 2010,
    "month" : "JANUARY",
    "dayOfMonth" : 1,
    "dayOfWeek" : "FRIDAY",
    "dayOfYear" : 1,
    "monthValue" : 1,
    "hour" : 2,
    "minute" : 2,
    "second" : 0,
    "nano" : 0,
    "chronology" : {
      "id" : "ISO",
      "calendarType" : "iso8601"
    }
  }

While I would like convert it to something like:

"startDate": "2015-01-01"

My code looks like this:

@JsonFormat(pattern="yyyy-MM-dd")
@DateTimeFormat(iso = DateTimeFormat.ISO.TIME)
public LocalDateTime getStartDate() {
    return startDate;
}

But either of the above annotations don't work, the date keeps getting formatted like above. Suggestions welcome!

Erastianism answered 29/4, 2015 at 23:8 Comment(0)
E
160

update: Spring Boot 2.x doesn't require this configuration anymore. I've written a more up to date answer here.


(This is the way of doing it before Spring Boot 2.x, it might be useful for people working on an older version of Spring Boot)

I finally found here how to do it. To fix it, I needed another dependency:

compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.4.0")

By including this dependency, Spring will automatically register a converter for it, as described here. After that, you need to add the following to application.properties:

spring.jackson.serialization.write_dates_as_timestamps=false

This will ensure that a correct converter is used, and dates will be printed in the format of 2016-03-16T13:56:39.492

Annotations are only needed in case you want to change the date format.

Erastianism answered 30/4, 2015 at 5:41 Comment(8)
Probably worth including the following annotation - @JsonSerialize(using = LocalDateTimeSerializer.class)...Fremont
Probably better to just use an application.properties entry, as suggested by @patelb answer.Deforce
Not working. But patelib's answer just works out of the box!Eno
As a heads up, ours needed the @JsonSerialize annotation Nick mentioned to get it working.Acrefoot
In my case this worked: @JsonSerialize(using = LocalDateSerializer.class) and @JsonFormat(pattern="yyyy-MM-dd"), no new dependencies and properties.Blip
The link in the update part is not workingWhew
This version did't solve for me. needed to use implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3'Gwennie
Spring Boot 2.x doesn't require this configuration anymore, this is not true, you still need it.Stanzel
E
97

I added the com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.1 dependency and started to get the date in the following format:

"birthDate": [
    2016,
    1,
    25,
    21,
    34,
    55
  ]

which is not what I wanted but I was getting closer. I then added the following

spring.jackson.serialization.write_dates_as_timestamps=false

to application.properties file which gave me the correct format that I needed.

"birthDate": "2016-01-25T21:34:55"
Electroencephalogram answered 27/1, 2016 at 7:20 Comment(2)
Worked out of the box when including the jackson-datatype-jsr310 dependency. This should be the accepted answer.Deforce
As an FYI, the application.properties part can be done through Java config if you're configuring the ObjectMapper with: mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);Naturalist
H
31

Here it is in maven, with the property so you can survive between spring boot upgrades

<dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>${jackson.version}</version>
</dependency>
Heliport answered 13/5, 2016 at 20:53 Comment(1)
Use this solution with @NickGrealy comment: Probably worth including the following annotation - @JsonSerialize(using = LocalDateTimeSerializer.class)Sewage
Q
26

1) Dependency

 compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.8.8' 

2) Annotation with date-time format.

public class RestObject {

    private LocalDateTime timestamp;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    public LocalDateTime getTimestamp() {
        return timestamp;
    }
}

3) Spring Config.

@Configuration
public class JacksonConfig {

    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
        System.out.println("Config is starting.");
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        return objectMapper;
    }
}
Quesenberry answered 5/6, 2017 at 12:27 Comment(2)
Thanks so much. @JsonFormat is the one that solved it for me. None of the above solutions worked for me for some reasonAerobic
Is there a similar config for ObjectWriter as I need to convert the object as string and the store to a DB but the LocalDateTime filed comes out as comma seperated.Headward
B
14

Writing this answer as a reminder for me as well.

I combined several answers here and in the end mine worked with something like these. (I am using SpringBoot 1.5.7 and Lombok 1.16.16)

@Data
public Class someClass {

   @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
   @JsonSerialize(using = LocalDateTimeSerializer.class)
   @JsonDeserialize(using = LocalDateTimeDeserializer.class)
   private LocalDateTime someDate;

}
Burns answered 10/10, 2018 at 6:15 Comment(5)
you might want to add imports for DateTimeFormat and others.Groundling
Where does the LocalDateTimeDeserializer come from?Scarito
You can check the accepted answer by Erik Pragt, he mentioned he added jackson dependency, that's where it comes from. Hopefully it answers your questionBurns
This solved my problem, thanks.Leilanileininger
Glad it helped mateBurns
I
10

I found another solution which you can convert it to whatever format you want and apply to all LocalDateTime datatype and you do not have to specify @JsonFormat above every LocalDateTime datatype. first add the dependency :

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

Add the following bean :

@Configuration
public class Java8DateTimeConfiguration {
    /**
     * Customizing
     * http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html
     *
     * Defining a @Bean of type Jackson2ObjectMapperBuilder will allow you to customize both default ObjectMapper and XmlMapper (used in MappingJackson2HttpMessageConverter and MappingJackson2XmlHttpMessageConverter respectively).
     */
    @Bean
    public Module jsonMapperJava8DateTimeModule() {
        val bean = new SimpleModule();

        bean.addDeserializer (ZonedDateTime.class, new JsonDeserializer<ZonedDateTime>() {
            @Override
            public ZonedDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
                return ZonedDateTime.parse(jsonParser.getValueAsString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
            }
        });

        bean.addDeserializer(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
            @Override
            public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
                return LocalDateTime.parse(jsonParser.getValueAsString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
            }
        });

        bean.addSerializer(ZonedDateTime.class, new JsonSerializer<ZonedDateTime>() {
            @Override
            public void serialize(
                    ZonedDateTime zonedDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                    throws IOException {
                jsonGenerator.writeString(DateTimeFormatter.ISO_ZONED_DATE_TIME.format(zonedDateTime));
            }
        });

        bean.addSerializer(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
            @Override
            public void serialize(
                    LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                    throws IOException {
                jsonGenerator.writeString(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(localDateTime));
            }
        });

        return bean;
    }
}

in your config file add the following :

@Import(Java8DateTimeConfiguration.class)

This will serialize and de-serialize all properties LocalDateTime and ZonedDateTime as long as you are using objectMapper created by spring.

The format that you got for ZonedDateTime is : "2017-12-27T08:55:17.317+02:00[Asia/Jerusalem]" for LocalDateTime is : "2017-12-27T09:05:30.523"

Inandin answered 27/12, 2017 at 6:33 Comment(5)
Probabaly you need to replace val bean to SimpleModule bean = new SimpleModule();Revamp
Is this for Java 9?Instrumental
@DanielDai This is in Java 8.Inandin
@Abhi, SimpleModule extends Module. Module is an abstract. val bean = new SimpleModule(); works perfect.Inandin
for SpringBoot version 1.5.3.RELEASE was not necessary to add the jackson-datatype-jsr310 dependency.Manumit
M
10

I am using Spring Boot 2.1.8. I have imported

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
</dependency>

which includes the jackson-datatype-jsr310.

Then, I had to add these annotations

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonProperty("date")
LocalDateTime getDate();

and it works. The JSON looks like this:

"date": "2020-03-09 17:55:00"
Milfordmilhaud answered 11/3, 2020 at 8:5 Comment(1)
Hint: including spring-boot-starter-json is only needed if spring-boot-starter-web is missing.Xylophagous
L
10

This worked for me.

I defined birthDate field in my DTO as mentioned below:

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

And in my request body, I passed birthDate in following format :

{
   "birthDate": "2021-06-03 00:00:00"
 }
Lactic answered 6/6, 2021 at 12:47 Comment(1)
This @JsonFormat(pattern="yyyy-MM-dd HH:mm") does work. And this does not work with yyyy-MM-dd hh:mmVocational
R
6

This work fine:

Add the dependency:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>

Add the annotation:

@JsonFormat(pattern="yyyy-MM-dd")

Now, you must get the correct format.

To use object mapper, you need register the JavaTime

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
Rational answered 5/7, 2018 at 19:38 Comment(0)
A
4

As already mentioned, spring-boot will fetch all you need (for both web and webflux starter).

But what's even better - you don't need to register any modules yourself. Take a look here. Since @SpringBootApplication uses @EnableAutoConfiguration under the hood, it means JacksonAutoConfiguration will be added to the context automatically. Now, if you look inside JacksonAutoConfiguration, you will see:

    private void configureModules(Jackson2ObjectMapperBuilder builder) {
        Collection<Module> moduleBeans = getBeans(this.applicationContext,
                Module.class);
        builder.modulesToInstall(moduleBeans.toArray(new Module[0]));
    }

This fella will be called in the process of initialization and will fetch all the modules it can find in the classpath. (I use Spring Boot 2.1)

Aristocracy answered 19/12, 2018 at 17:4 Comment(0)
L
4

This worked for me.

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
public Class someClass {

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    private LocalDateTime sinceDate;

}

Lysenko answered 7/12, 2020 at 20:13 Comment(0)
F
3

Following Annotations works for me

@JsonSerialize(using = LocalDateSerializer.class) and
@JsonFormat(pattern="yyyy-MM-dd")
Floris answered 14/3, 2022 at 17:8 Comment(0)
D
1

I am using Springboot 2.0.6 and for some reason, the app yml changes did not work. And also I had more requirements.

I tried creating ObjectMapper and marking it as Primary but spring boot complained that I already have jacksonObjectMapper as marked Primary!!

So this is what I did. I made changes to the internal mapper.

My Serializer and Deserializer are special - they deal with 'dd/MM/YYYY'; and while de-serializing - it tries its best to use 3-4 popular format to make sure I have some LocalDate.

@Autowired
ObjectMapper mapper;

@PostConstruct
public ObjectMapper configureMapper() {
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);

    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

    mapper.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, true);
    mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);

    SimpleModule module = new SimpleModule();
    module.addDeserializer(LocalDate.class, new LocalDateDeserializer());
    module.addSerializer(LocalDate.class, new LocalDateSerializer());
    mapper.registerModule(module);

    return mapper;
}
Dirigible answered 17/4, 2019 at 20:23 Comment(0)
T
0

@JsonDeserialize(using= LocalDateDeserializer.class) does not work for me with the below dependency.

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version> 2.9.6</version>
</dependency>

I have used the below code converter to deserialize the date into a java.sql.Date.

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;


@SuppressWarnings("UnusedDeclaration")
@Converter(autoApply = true)
public class LocalDateConverter implements AttributeConverter<java.time.LocalDate, java.sql.Date> {


    @Override
    public java.sql.Date convertToDatabaseColumn(java.time.LocalDate attribute) {

        return attribute == null ? null : java.sql.Date.valueOf(attribute);
    }

    @Override
    public java.time.LocalDate convertToEntityAttribute(java.sql.Date dbData) {

        return dbData == null ? null : dbData.toLocalDate();
    }
}
Tomasz answered 27/9, 2018 at 13:55 Comment(0)
L
0

Added

group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.8.8'

into gradle compile block

and in the application.yml file

spring:
  jackson:
    serialization:
      write_dates_as_timestamps: false

if you are using application.properties file add the following

spring.jackson.serialization.write_dates_as_timestamps=false

in case you want to apply a custom format you can apply the annotation

    @JsonFormat(pattern = "yyyy-MMMM-dd hh:mm:ss")
    private LocalDateTime date;

It started working fine for me

Loch answered 27/4, 2021 at 16:7 Comment(0)
H
0

If you want to configure it globally without using properties defined in application.properties file as noted by other answers, you can provide your own jackson mapper and configure date format of it like so:

@Configuration
public class SpringConfig {

    @Bean
    public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
        return new Jackson2ObjectMapperBuilder()
                .dateFormat(new StdDateFormat());
    }
}
Hemminger answered 18/10, 2023 at 9:46 Comment(0)
O
-2

simply use:

@JsonFormat(pattern="10/04/2019")

or you can use pattern as you like for e.g: ('-' in place of '/')

Offhand answered 29/4, 2019 at 6:30 Comment(1)
This answer has been shared before, but then with the correct syntax.Erastianism

© 2022 - 2024 — McMap. All rights reserved.