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!
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.
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"
jackson-datatype-jsr310
dependency. This should be the accepted answer.
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
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>
@JsonSerialize(using = LocalDateTimeSerializer.class)
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;
}
}
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;
}
DateTimeFormat
and others.
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"
SimpleModule
extends Module
. Module
is an abstract. val bean = new SimpleModule();
works perfect.
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"
spring-boot-starter-json
is only needed if spring-boot-starter-web
is missing.
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());
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"
}
@JsonFormat(pattern="yyyy-MM-dd HH:mm")
does work. And this does not work with yyyy-MM-dd hh:mm
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)
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;
}
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;
}
@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();
}
}
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
Following Annotations works for me
@JsonSerialize(using = LocalDateSerializer.class) and
@JsonFormat(pattern="yyyy-MM-dd")
simply use:
@JsonFormat(pattern="10/04/2019")
or you can use pattern as you like for e.g: ('-' in place of '/')
Success story sharing
@JsonSerialize(using = LocalDateTimeSerializer.class)
...application.properties
entry, as suggested by @patelb answer.@JsonSerialize
annotation Nick mentioned to get it working.@JsonSerialize(using = LocalDateSerializer.class)
and@JsonFormat(pattern="yyyy-MM-dd")
, no new dependencies and properties.