Learn to fix the error ‘Java 8 date/time type not supported by default‘ while serializing and deserializing Java 8 Date time classes using Jackson.
1. Problem
The error occurs when we serialize a Java object or deserialize JSON to POJO, and the POJO contains new Java 8 date time classes such as LocalDate, LocalTime, LocalDateTime etc.
For example, the following Employee class has LocalDate type field.
public class Employee {
private Long id;
private String name;
private LocalDate dateOfBirth;
}
When we serialize an instance of this class, we get the following exception:
Exception in thread "main" java.lang.IllegalArgumentException: Java 8 date/time type `java.time.LocalDate` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.howtodoinjava.core.objectToMap.Employee["dateOfBirth"])
at com.fasterxml.jackson.databind.ObjectMapper ._convert(ObjectMapper.java:4393)
at com.fasterxml.jackson.databind.ObjectMapper .convertValue(ObjectMapper.java:4324)
at com.howtodoinjava.core.objectToMap .ObjectToMapUsingJackson.main(ObjectToMapUsingJackson.java:25)
2. Solution
We must add support to new Java 8 classes in two steps to fix this error.
First, add the latest version of com.fasterxml.jackson.datatype:jackson-datatype-jsr310 Maven dependency.
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.4</version>
</dependency>
Second, register the module JavaTimeModule either with ObjectMapper or JsonMapper based on what you are using.
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
//or
JsonMapper jsonMapper = new JsonMapper();
jsonMapper.registerModule(new JavaTimeModule());
After registering the JavaTimeModule, the above error will go away.
3. With Hibernate 6
The above solution may not work if you are facing this issue due to Hibernate 6 that serializes the objects using FormatMapper instances, and the default is JacksonJsonFormatMapper. The JacksonJsonFormatMapper basically uses a Jackson ObjectMapper instance constructed without any additional options.
To fix the issue, we need to create a custom instance of FormatMapper and assign it through a new property hibernate.type.json_format_mapper.
spring.jpa.properties.hibernate.type.json_format_mapper=com.howtodoinjava.CustomJacksonJsonFormatMapper
And then create the mapper implementation as follows:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.hibernate.type.FormatMapper;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.jackson.JacksonJsonFormatMapper;
public class JacksonJsonFormatMapperCustom implements FormatMapper {
private final FormatMapper delegate;
public JacksonJsonFormatMapperCustom() {
ObjectMapper objectMapper = createObjectMapper();
delegate = new JacksonJsonFormatMapper(objectMapper);
}
private static ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return objectMapper;
}
@Override
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
return delegate.fromString(charSequence, javaType, wrapperOptions);
}
@Override
public <T> String toString(T t, JavaType<T> javaType, WrapperOptions wrapperOptions) {
return delegate.toString(t, javaType, wrapperOptions);
}
}
Happy Learning !!
thankyou so much , it works fine this code:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
fixed it by implementing a CustomJsonProvider which uses my ObjectMapper with registered JavaTimeModule:
@Provider @Priority(value = 1) @Consumes({"application/json", "application/*+json", "text/json"}) @Produces({"application/json", "application/*+json", "text/json"}) public class CustomJsonProvider extends JacksonJsonProvider { private static final ObjectMapper OBJECT_MAPPER; static { // create the one and only instance OBJECT_MAPPER = LuminObjectMapper.getInstance(); } public CustomJsonProvider() { super(OBJECT_MAPPER); } }Thanks for sharing.
Works for me, when using Postman to send GET and PUT requests! But does not work, when using REST-Client from my WebApp to send PUT request. This results in error:
RESTEASY004655: Unable to invoke request: java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type
java.time.LocalDateTimenot supported by defaultBut I have registered following modules as seen in my server.log:
a.l.m.c.r.LuminObjectMapper: registered module ‘com.fasterxml.jackson.datatype.jdk8.Jdk8Module’
a.l.m.c.r.LuminObjectMapper: registered module ‘com.fasterxml.jackson.datatype.jsr310.JavaTimeModule’
a.l.m.c.r.LuminObjectMapper: registered module ‘com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule’
Probably a problem with resteasy client an Jackson ?!
Any hints welcome !
Thanx and regards,
Rainer
can we register JodaMoudle also, along with JavaTimeModule, as the project is already using the joda time, from now we need to use java time, please suggest
Yes, you can register both modules as well. It will work fine.
Thank you so much for the quick reply.
Does not work with Hibernate 6.
Thanks for sharing. Much appreciated !