apache / camel-quarkus

Apache Camel Quarkus
https://camel.apache.org
Apache License 2.0
250 stars 186 forks source link

JSON/Jackson: How to configure JavaTimeModule in Quarkus Camel? #4412

Open tmulle opened 1 year ago

tmulle commented 1 year ago

Hi,

Just started using Quarkus-Camel and am still learning how things work.

I am trying to serialize a POJO with LocalDateTime and getting the error below.

I've done a ton of searching and found various answers for Quarkus itself here: https://quarkus.io/guides/rest-json#about-serialization

I've tried both options to provide an ObjectMapper configuration and I still get the error.

I've included the dependency as well:

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

I also looked here: https://camel.apache.org/components/3.20.x/dataformats/jackson-dataformat.html but didn't find anything useful. I tried to set the module class parameter in application.properties but that didn't seem to help.

camel.dataformat.jackson.auto-discover-object-mapper = true
camel.dataformat.jackson.module-class-names = com.fasterxml.jackson.datatype.jsr310.JavaTimeModule

Any ideas how to get this to work? It will be helpful in the future should I need to add other configuration options.

 com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.acme.SnapshotEntity["uploadDate"])
        at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
        at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1306)
        at com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer.serialize(UnsupportedTypeSerializer.java:35)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:733)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774)
        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.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1572)
        at com.fasterxml.jackson.databind.ObjectWriter._writeValueAndClose(ObjectWriter.java:1273)
        at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1098)
        at org.apache.camel.component.jackson.AbstractJacksonDataFormat.marshal(AbstractJacksonDataFormat.java:155)
        at org.apache.camel.support.processor.MarshalProcessor.process(MarshalProcessor.java:64)
        at org.apache.camel.processor.errorhandler.RedeliveryErrorHandler$SimpleTask.run(RedeliveryErrorHandler.java:477)
        at org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.schedule(DefaultReactiveExecutor.java:181)
        at org.apache.camel.impl.engine.DefaultReactiveExecutor.scheduleMain(DefaultReactiveExecutor.java:59)
        at org.apache.camel.processor.Pipeline.process(Pipeline.java:175)
        at org.apache.camel.impl.engine.CamelInternalProcessor.process(CamelInternalProcessor.java:392)
        at org.apache.camel.component.platform.http.vertx.VertxPlatformHttpConsumer.lambda$handleRequest$2(VertxPlatformHttpConsumer.java:193)
        at io.vertx.core.impl.ContextBase.lambda$null$0(ContextBase.java:137)
        at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:264)
        at io.vertx.core.impl.ContextBase.lambda$executeBlocking$1(ContextBase.java:135)
        at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:833)
jamesnetherton commented 1 year ago

Are you using the Camel REST DSL? If so, try this in your RouteBuilder:

restConfiguration().dataFormatProperty("autoDiscoverObjectMapper", "true");
djcoleman commented 1 year ago

There is more information on the Camel Quarkus Jackson page. Inspired by this answer on StackOverflow, you can add the following to your RouteBuilder if not using REST DSL:

JacksonDataFormat df = new JacksonDataFormat();
df.setModuleClassNames("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule");
df.setAutoDiscoverObjectMapper(true);

Then add marshal(df) to your route, for example:

from("direct:start").marshal(df).log("${body}");
tmulle commented 1 year ago

Thanks for the info, unfortunately it still doesn't appear to be working. I am using the REST DSL and added the properties you mentioned but I still get the same error.

It appears that manually creating the JacksonDataFormat and changing my marshaling from .marshal().json to just .marshal(df) as suggested by @djcoleman seems to work and the JSON is rendered.

Just wish it would work using the REST DSL.. seems like it should but I see from other Stack Overflow posts people having issues as well using the REST DSL, even with SpringBoot. There are very few posts about Camel and Quarkus. Most results are using SpringBoot.

This doesn't work..

Marshalling from("direct:displayAll").process(this::displayAll).marshal().json();

REST DSL config

restConfiguration()
                .contextPath("/archive")
                .skipBindingOnErrorCode(true)
                .enableCORS(true)
                .dataFormatProperty("prettyPrint", "true")
                .dataFormatProperty("autoDiscoverObjectMapper", "true")
                .dataFormatProperty("moduleClassNames", "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule");

But this does work:

REST DSL Config

restConfiguration()
                .contextPath("/archive")
                .enableCORS(true);

Marshalling from("direct:displayAll").process(this::displayAll).marshal(df);

Manually Creating the JacksonDataFormat

JacksonDataFormat df = new JacksonDataFormat();
        df.setModuleClassNames("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule");
        df.setAutoDiscoverObjectMapper(true);
grigoriliev commented 9 months ago

I was having the same problem using Spring Boot. My issue was that I didn't have the following Maven dependency:

 <dependency>
    <groupId>org.apache.camel.springboot</groupId>
    <artifactId>camel-jackson-starter</artifactId>
 </dependency>

Without it there is no support for auto configuration. In result, the configuration

camel.dataformat.jackson.module-class-names = com.fasterxml.jackson.datatype.jsr310.JavaTimeModule

in application.properties was ignored.