FasterXML / jackson-modules-java8

Set of support modules for Java 8 datatypes (Optionals, date/time) and features (parameter names)
Apache License 2.0
399 stars 116 forks source link

Exception with OffsetDateTime and jsr310 datatype version > 2.11.4 #219

Closed sven-v-t-veer closed 3 years ago

sven-v-t-veer commented 3 years ago

I'm using the jackson-datatype-jsr310 module in my (spring) application. The issue occurs with any version over 2.11.4. I have simplified the issue: POJO:

import java.time.OffsetDateTime;

public class Pojo {
    OffsetDateTime aDateTime;

    public OffsetDateTime getaDateTime() {
        return aDateTime;
    }

    public void setaDateTime(OffsetDateTime aDateTime) {
        this.aDateTime = aDateTime;
    }
}

Application:

@SpringBootApplication
public class OffsetTimeTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(OffsetTimeTestApplication.class, args);
    }

    public OffsetTimeTestApplication() {
        ObjectMapper mapper = new ObjectMapper();
        Pojo pojo = new Pojo();
        pojo.setaDateTime(Instant.now().atOffset(ZoneOffset.UTC));
        try {
            System.out.println("output: " + mapper.writeValueAsString(pojo));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }

}

Relevant section of build.gradle:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.12.3'
    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.12.3'
    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.3'
    implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.12.3'

Stacktrace:

2021-06-01 10:27:14.724  INFO 235160 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.OffsetDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.enervalis.offsettimetest.Pojo["aDateTime"])
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
    at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1276)
    at com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer.serialize(UnsupportedTypeSerializer.java:35)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
    at com.fasterxml.jackson.databind.ObjectMapper._writeValueAndClose(ObjectMapper.java:4487)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3742)
    at com.enervalis.offsettimetest.OffsetTimeTestApplication.<init>(OffsetTimeTestApplication.java:23)
    at com.enervalis.offsettimetest.OffsetTimeTestApplication$$EnhancerBySpringCGLIB$$e0b6f1dc.<init>(<generated>)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:212)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1308)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1214)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:438)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:337)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1336)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1325)
    at com.enervalis.offsettimetest.OffsetTimeTestApplication.main(OffsetTimeTestApplication.java:15)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)

By changing the version to 2.11.4, the output is:

output: {"aDateTime":{"offset":{"totalSeconds":0,"id":"Z","rules":{"transitions":[],"transitionRules":[],"fixedOffset":true}},"nano":641484000,"year":2021,"monthValue":6,"dayOfMonth":1,"hour":8,"minute":30,"second":32,"dayOfWeek":"TUESDAY","dayOfYear":152,"month":"JUNE"}}

cowtowncoder commented 3 years ago

Your problem is that you do not register Java 8 date/time module with ObjectMapper you are using: you need to do one of:

// newer, 2.10+
ObjectMapper mapper = JsonMapper.builder()
   .addModule(new JavaTimeModule())
   .build();

// or older pre-2.10 (works still with 2.10+)
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());

2.12 will actually prevent reading/writing of java.time. values without explicitly registered handlers because previously (as you saw) serialization would assume these are simple POJO values that then can not be read back at all.

sven-v-t-veer commented 3 years ago

Thanks!

I'm just upgrading some legacy code, upgrading from Jackson version 2.8.2.

It's clear to me now that it has always been used wrong.

cowtowncoder commented 3 years ago

@sven-v-t-veer Ah. Yes, these cases can be tricky. I hope you can get the issue resolved.