Issue
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?
Solution
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 anObjectMapper
without an unmarshal type. However, in the default constructor aHashMap.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 theObjectMapper
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 theDataFormat
. See CAMEL-7880.
Answered By - David Edwards
Answer Checked By - David Goodson (JavaFixing Volunteer)