18

After update from Spring Boot 2.4.5 to Spring 2.5.0 I noticed the following exceptions in the application logs:

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.Instant` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: org.telegram.telegrambots.meta.api.objects.Update["my_chat_member"]->org.telegram.telegrambots.meta.api.objects.ChatMemberUpdated["new_chat_member"]->org.telegram.telegrambots.meta.api.objects.ChatMember["untilDateAsInstant"]) at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1276) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer.serialize(UnsupportedTypeSerializer.java:35) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1514) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ObjectWriter._writeValueAndClose(ObjectWriter.java:1215) ~[jackson-databind-2.12.3.jar!/:2.12.3] at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1059) ~[jackson-databind-2.12.3.jar!/:2.12.3] at org.springframework.jms.support.converter.MappingJackson2MessageConverter.mapToTextMessage(MappingJackson2MessageConverter.java:279) ~[spring-jms-5.3.7.jar!/:5.3.7] at org.springframework.jms.support.converter.MappingJackson2MessageConverter.toMessage(MappingJackson2MessageConverter.java:184) ~[spring-jms-5.3.7.jar!/:5.3.7] ... 37 common frames omitted 

this is my pom.xml:

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

I reverted to Spring Boot 2.4.5 and not everything works fine. What may be wrong with Spring Boot 2.5.0 ?

Updated

The corresponding GitHub https://github.com/spring-projects/spring-boot/issues/26859

22
  • 2
    Just adding the dependency is not enough. You need to configure the object mapper to add the Jackson module Commented Jun 7, 2021 at 15:34
  • 1
    But why it is working in SB 2.4.5 in stopped working in 2.5.0 ? Commented Jun 7, 2021 at 15:44
  • 1
    Personally I would wait until the next release. In addition to this issue, 2.5.0 also broke our application due to: github.com/spring-projects/spring-boot/issues/26627 Commented Jun 10, 2021 at 7:05
  • 1
    For me - the /actuator/info endpoint stopped working after upgrading to sb 2.5.1, and fail with the same InvalidDefinitionException and jsr310 error regarding Instant type. Is the commit pointed by @Michael solved this issue ? Commented Jun 14, 2021 at 11:59
  • 1
    @AndyWilkinson you were right. After deep investigation I found the following code in the 3rdparty library = new ObjectMapper(); I added the following fix ObjectMapper o = new ObjectMapper().registerModule(new JavaTimeModule()); and the issue is gone. Thank you! Commented Jul 13, 2021 at 11:50

7 Answers 7

39

I saw an issue in one of my test classes. The problem there was it was creating a new ObjectMapper instance that was not adding the JavaTimeModule.

Here is a sample test that works in Spring 2.4.5 but fails in 2.5.0/2.5.1 with com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type java.time.ZonedDateTime not supported by default

It might be due to the upgrade in the jackson-datatype-jsr310 version

package net.jpmchase.gti.gtfabric; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import java.time.ZonedDateTime; public class ObjectMapperTest { @Test public void objectMapperTest() throws Exception{ ZonedDateTime time = ZonedDateTime.now(); ObjectMapper o = new ObjectMapper(); o.writeValueAsString(time); } } 

To fix this particular test case had to add an explicit

ObjectMapper o = new ObjectMapper(); o.registerModule(new JavaTimeModule()); 
Sign up to request clarification or add additional context in comments.

4 Comments

Try to use Jackson2ObjectMapperBuilder class to build instances of the ObjectMapper class: Jackson2ObjectMapperBuilder.json().build(). In this case you would obtain properly configured mappers. Wherever you have Spring Context, you could also inject a bean of ObjectMapper which Spring Boot exposes via JacksonAutoConfiguration.
I had this problem evaluating values on unit test and this fixed it doing new ObjectMapper().registerModule(new JavaTimeModule()).writeValueAsString(value); :D thanks
@TuGordoBello aggree and advice to use the fluent-mutator to initialize in one line: new ObjectMapper().registerModule(new JavaTimeModule()).
This isn't an answer just tells you how to reproduce the issue. Doesn't help if your dependecies are using ObjectMapper. Nothing will stop spring from picking up the broken ObjectMapper.
12

Perhaps you are not using in your code Object Mapper provided by spring.

Wrong way:

ObjectMapper o = new ObjectMapper(); 

Correct way:

@Autowired Jackson2ObjectMapperBuilder mapperBuilder; 

...

ObjectMapper mapper = mapperBuilder.build(); 

4 Comments

Can you explain, why the constructor use is considered wrong and with injected builder correct ?
If you are creating an instance yourself, you will need to configure everything yourself, for example add all the extensions you need. Using the builder provided by Spring, you have everything set up.
Would add this great explanation to your answer 👍️ Spring's auto-configuration already registers available modules like JavaTimeModule to its default ObjectMapper bean. You can simply use this by adding a field to the test-class: @Autowired ObjectMapper mapper; (see related answer). I would only use the builder if Spring's auto-configured ObjectMapper is not sufficient and I need to customize a separate one.
Not helpful because you can't change your depdencies code to use Jackson2ObjectMapperBuilder.
2

In case you are using Spring Data Couchbase then this might be your problem: https://github.com/spring-projects/spring-data-couchbase/blame/4.2.x/src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java#L309

The bug report is here: https://github.com/spring-projects/spring-data-couchbase/issues/1209

This has been fixed in Spring-Data-Couchbase 4.3

Comments

2

Posting a proper answer for the future me (that forgets):

@Configuration public class JacksonConfig { @Bean public BatchConfigurer batchConfigurer(DataSource dataSource, PlatformTransactionManager transactionManager) { return new DefaultBatchConfigurer(dataSource) { @Override protected JobRepository createJobRepository() throws Exception { Jackson2ExecutionContextStringSerializer serializer = new Jackson2ExecutionContextStringSerializer(); ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()).findAndRegisterModules(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true); serializer.setObjectMapper(objectMapper); JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean(); factory.setDataSource(dataSource); factory.setTransactionManager(transactionManager); factory.setSerializer(serializer); return factory.getObject(); } }; } 

}

Source: https://docs.spring.io/spring-batch/docs/current/reference/html/job.html

Comments

2

I faced the same issue with Spring 3.1.2, after trying different approaches the solution that worked for me was composed of 2 changes:

  • Adding the dependency as follows (this project uses gradle)

implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2'

  • Adding this annotation to the Instant property that was causing the problem

@JsonSerialize(using = InstantSerializer.class)

Comments

0

While both Saifuddin Merchant and Dawid Świtoń-Maniakowski have provided different but equally working ways to make the library

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

actually work, my proposal is to remove it from your pom.xml completely and to write your own serializer (as an inner static class in this example):

public static class CustomDateSerializer extends com.fasterxml.jackson.databind.ser.std.StdSerializer<LocalDateTime> { private static final long serialVersionUID = 4365897561L; public CustomDateSerializer() { this(null); } public CustomDateSerializer(Class<LocalDateTime> t) { super(t); } @Override public void serialize( LocalDateTime value, JsonGenerator gen, SerializerProvider arg2) throws IOException, JsonProcessingException { gen.writeString(value.format(DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm:ss"))); } } 

Next, annotate the time field of the object you want to serialize as JSON:

@JsonSerialize(using = CustomDateSerializer.class) private LocalDateTime time = LocalDateTime.now(); 

Not much of code, right? The pom.xml dependency is 5 lines of code itself. But then autowiring Jackson2ObjectMapperBuilder is 2 lines, or if you decide to go with registering new JavaTimeModule() then it would be much more lines of code since this JavaTimeModule provides inappropriate date time serialization (as an array of numbers!) out of the box, so you would be in need to wire your own formatter into this JavaTimeModule somehow... not what you want probably.

Watch out for the generic type of the custom serializer - it is suited for <LocalDateTime> here. Just modify it to fit your needs.

Comments

0

You can just @Autowire the ObjectMapper.

@Autowired private ObjectMapper om; 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.