I am trying to serialize and deserialize POJOs to and from JSON on Camel routes using Jackson. Some of these have Java 8 LocalDate fields, and I want them to be serialised as YYYY-MM-DD string, not as an array of integers.
We only use Java configuration for our Spring Boot application, so no XML Camel configuration.
I have successfully created an ObjectMapper that does what I want, which is being used by other parts of our system by adding this to our dependencies:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
and this to our application configuration:
@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
return builder
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.build();
}
Example outgoing REST route:
@Component
public class MyRouteBuilder extends RouteBuilder {
@Override
public void configure() throws Exception {
restConfiguration().component("servlet").contextPath("/mycontext")
.port(8080).bindingMode(RestBindingMode.json);
rest("/myendpoint)
.get()
.route()
.to("bean:myService?method=myMethod()");
}
}
Example incoming message route:
@Component
public class MyRouteBuilder extends RouteBuilder {
@Autowired
private MyBean myBean;
@Override
public void configure() {
from(uri)
.unmarshal().json(JsonLibrary.Jackson)
.bean(myBean);
}
}
However, by default Camel creates its own ObjectMapper instances so does not pick up on either the JSR310 serializers/deserializers that Jackson2ObjectMapperBuilder
adds automatically, or the disabled WRITE_DATES_AS_TIMESTAMPS
feature. I have read the Camel JSON documentation, but it does not show how to add a custom DataFormat using Spring configuration, or how to apply a global customisation for all types.
So how can I tell Camel to use my ObjectMapper, using only Spring Boot Java configuration?
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:
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.SCOPE_PROTOTYPE
because the REST DSL expects to get a new instance of the DataFormat
. See CAMEL-7880.