How to configure Jackson ObjectMapper for Camel in Spring Boot

I have found a solution by stepping through the Camel code. So while it does what I want, it might not work with future versions of Camel since it appears to be undocumented and potentially unsupported.

All I do is add the following bean to my Spring config, in additional to my ObjectMapper bean in the question:

@Bean(name = "json-jackson")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public JacksonDataFormat jacksonDataFormat(ObjectMapper objectMapper) {
    return new JacksonDataFormat(objectMapper, HashMap.class);
}

The crucial points to note:

  • There is no constructor for JacksonDataFormat that takes an ObjectMapper without an unmarshal type. However, in the default constructor a HashMap.class is used when no unmarshal type is provided, so I use that. By some magic, this appears to then get used to unmarshal all POJO types. If you also need more specific data formats for other classes, you will need to set the ObjectMapper in them too.
  • Camel appears to search the bean registry for a bean called "json-jackson", so setting the Spring bean to use that name tricks Camel into not creating a new one and using mine instead.
  • The bean scope must be set to SCOPE_PROTOTYPE because the REST DSL expects to get a new instance of the DataFormat. See CAMEL-7880.

Good news everyone, object mapper autodiscovery is supported now for Spring Boot! Simply set this property:

camel.dataformat.json-jackson.auto-discover-object-mapper=true

If set to true then Jackson will lookup for an objectMapper into the registry

Docs: https://camel.apache.org/components/latest/dataformats/json-jackson-dataformat.html#_spring_boot_auto_configuration

Logs:

INFO o.a.c.impl.engine.AbstractCamelContext   : Apache Camel 3.3.0 (CamelContext: camel-1) is starting
INFO o.a.c.c.jackson.JacksonDataFormat        : Found single ObjectMapper in Registry to use: com.fasterxml.jackson.databind.ObjectMapper@20a1b3ae
WARN o.a.c.c.jackson.JacksonDataFormat        : The objectMapper was already found in the registry, no customizations will be applied

(the warning just denotes, that all your other properties under camel.dataformat.json-jackson.* are ignored)

Update 17.08.2022

For new versions of Camel since 3.15.0 use different property

camel.dataformat.jackson.auto-discover-object-mapper=true

Create the JacksonDataFormat in java code and enable/disable the features you want, and then use that instance in the Camel route.

 .unmarshal(myInstanceGoesHere).

This is an example when using marshal, the same can be adapter with unmarshal:

CamelContext ctx = new DefaultCamelContext();
JacksonDataFormat df = new JacksonDataFormat();

df.setModuleClassNames("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule");

ctx.addRoutes(
    new RouteBuilder() {
      @Override
      public void configure() {
        from("direct:start").marshal(df).log("Out");
      }
    });

Using Spring and Camel 2.18.1, I was able to achieve the same by adding the following dependencies:

<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.6.1</version>
</dependency>

and in a CamelContextConfiguration class, autowiring the JacksonDataFormat in order to configure the discovery of classpath modules and the configuration of the serialization options:

@Configuration
public class CamelContextConfig implements CamelContextConfiguration {

    @Autowired
    public JacksonDataFormat jacksonDataFormat;

    @Override
    public void beforeApplicationStart(CamelContext camelContext) {
    }

    @Override
    public void afterApplicationStart(CamelContext camelContext) {
        jacksonDataFormat
            .getObjectMapper()
            .findAndRegisterModules()
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}