[英]How to configure Jackson ObjectMapper for Camel in Spring Boot
I am trying to serialize and deserialize POJOs to and from JSON on Camel routes using Jackson.我正在尝试使用 Jackson 在骆驼路线上序列化和反序列化与 JSON 之间的 POJO。 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.其中一些有 Java 8 LocalDate 字段,我希望它们被序列化为 YYYY-MM-DD 字符串,而不是整数数组。
We only use Java configuration for our Spring Boot application, so no XML Camel configuration.我们仅将 Java 配置用于我们的 Spring 引导应用程序,因此没有 XML 骆驼配置。
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:我已经成功地创建了一个 ObjectMapper 来做我想做的事,通过将它添加到我们的依赖项中,我们系统的其他部分正在使用它:
<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:传出 REST 路由示例:
@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.但是,默认情况下,Camel 创建自己的 ObjectMapper 实例,因此不会使用Jackson2ObjectMapperBuilder
自动添加的 JSR310 序列化器/反序列化器或禁用的WRITE_DATES_AS_TIMESTAMPS
功能。 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.我已阅读Camel JSON文档,但它没有显示如何使用 Spring 配置添加自定义数据格式,或如何为所有类型应用全局自定义。
So how can I tell Camel to use my ObjectMapper, using only Spring Boot Java configuration?那么如何告诉 Camel 使用我的 ObjectMapper,仅使用 Spring 引导 Java 配置?
I have found a solution by stepping through the Camel code.我通过单步执行 Camel 代码找到了解决方案。 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.因此,虽然它可以满足我的要求,但它可能不适用于 Camel 的未来版本,因为它似乎没有记录并且可能不受支持。
All I do is add the following bean to my Spring config, in additional to my ObjectMapper
bean in the question:我所做的就是将以下 bean 添加到我的 Spring 配置中,以及问题中的ObjectMapper
bean:
@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. JacksonDataFormat
没有采用没有解组类型的ObjectMapper
构造函数。 However, in the default constructor a HashMap.class
is used when no unmarshal type is provided, so I use that.但是,在默认构造函数中,当未提供解组类型时使用HashMap.class
,所以我使用它。 By some magic, this appears to then get used to unmarshal all POJO types.通过某种魔法,这似乎习惯于解组所有 POJO 类型。 If you also need more specific data formats for other classes, you will need to set the ObjectMapper
in them too.如果您还需要其他类的更具体的数据格式,您也需要在其中设置ObjectMapper
。SCOPE_PROTOTYPE
because the REST DSL expects to get a new instance of the DataFormat
. bean 范围必须设置为SCOPE_PROTOTYPE
因为 REST DSL 期望获得DataFormat
的新实例。 See CAMEL-7880 .参见CAMEL-7880 。在 Java 代码中创建JacksonDataFormat
并启用/禁用您想要的功能,然后在 Camel 路由中使用该实例。
.unmarshal(myInstanceGoesHere).
Using Spring and Camel 2.18.1, I was able to achieve the same by adding the following dependencies:使用 Spring 和 Camel 2.18.1,我能够通过添加以下依赖项来实现相同的目标:
<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:在CamelContextConfiguration
类中,自动JacksonDataFormat
以配置类路径模块的发现和序列化选项的配置:
@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);
}
}
Good news everyone, object mapper autodiscovery is supported now for Spring Boot!大家好消息,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如果设置为 true,那么 Jackson 将在注册表中查找 objectMapper
Docs: https://camel.apache.org/components/latest/dataformats/json-jackson-dataformat.html#_spring_boot_auto_configuration文档: 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) (警告只是表示, camel.dataformat.json-jackson.*
下的所有其他属性都被忽略)
So far only the suggestion of @david-edwards has worked for me.到目前为止,只有@david-edwards 的建议对我有用。 I first defined a data format bean with the id: "json-jackson"我首先定义了一个id为“json-jackson”的数据格式bean
<bean id="json-jackson" class="com.mydomain.JacksonDataFormatExt" />
Then the format class:然后是格式类:
public class JacksonDataFormatExt extends JacksonDataFormat{
public JacksonDataFormatExt(){
super();
setPrettyPrint(true);
setEnableFeatures(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS.name());
SimpleModule s = new SimpleModule();
s.addSerializer(CustomEnum.class, new CustomEnumSerializer());
addModule(s);
}
}
And the CustomEnumSerializer class:和 CustomEnumSerializer 类:
public class CustomEnumSerializer extends JsonSerializer<CustomEnum> {
@Override
public void serialize(CustomEnumvalue, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
String stringValue = value.getNlsText();
if(stringValue != null && !stringValue.isEmpty() && !stringValue.equals("null")) {
jgen.writeString(stringValue);
} else {
jgen.writeNull();
}
}
}
I managed to configure ObjectMapper for Camel quite conveniently using org.apache.camel:camel-jackson-starter:2.20.0
我设法使用org.apache.camel:camel-jackson-starter:2.20.0
非常方便地为 Camel 配置了 ObjectMapper
It exposes some of the useful ObjectMapper properties for configuration via Spring application properties.它通过 Spring 应用程序属性公开了一些有用的 ObjectMapper 属性以进行配置。 WRITE_DATES_AS_TIMESTAMPS for example can be set straight from application.yaml or application.properties file.例如,可以直接从 application.yaml 或 application.properties 文件设置 WRITE_DATES_AS_TIMESTAMPS。
Look for JacksonDataFormatConfiguration class for more details.查找 JacksonDataFormatConfiguration 类以获取更多详细信息。
I also needed to use some Mixins so I still needed to configure Camel to use a Spring's ObjectMapper.我还需要使用一些 Mixin,所以我仍然需要配置 Camel 以使用 Spring 的 ObjectMapper。 I ended up with this:我结束了这个:
Configuration bean:配置豆:
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return new Jackson2ObjectMapperBuilderCustomizer() {
@Override
public void customize(Jackson2ObjectMapperBuilder builder) {
builder.mixIn(Person.class, PersonMixin.class);
}
}
}
application.yaml:应用程序.yaml:
camel:
dataformat:
json-jackson:
disable-features: WRITE_DATES_AS_TIMESTAMPS
object-mapper: jacksonObjectMapper
Where jacksonObjectMapper
is the name of the ObjectMapper bean built by the configured Jackson2ObjectMapperBuilder其中jacksonObjectMapper
是配置的 Jackson2ObjectMapperBuilder 构建的 ObjectMapper bean 的名称
If anyone else was wondering how to use the fix put in the ver.如果其他人想知道如何使用版本中的修复程序。 2.17.. I got it working using this xml configuration: 2.17 .. 我使用这个 xml 配置让它工作:
<camel:camelContext id="defaultCamelContext">
.....
<camel:dataFormats>
<camel:json id="json" library="Jackson" objectMapper="myObjectMapper"/>
</camel:dataFormats>
</camel:camelContext>
..where myObjectMapper is a name of a spring bean of type ObjectMapper ..其中 myObjectMapper 是 ObjectMapper 类型的 spring bean 的名称
I solved by including jackson dependency in pom我通过在 pom 中包含 jackson 依赖来解决
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson-starter</artifactId>
<version>${camel.version}</version>
</dependency>
Now, simply adding JacksonDataFormat in route configuration现在,只需在路由配置中添加 JacksonDataFormat
public void configure() throws Exception {
JacksonDataFormat jsonDf = new JacksonDataFormat(Card.class);
jsonDf.setPrettyPrint(true);
from("direct:endpoint")
.marshal(jsonDf)
.convertBodyTo(String.class)
.....
}
If Camel gives you trouble there, I would revert to using beans directly:如果 Camel 在那里给你带来麻烦,我会恢复直接使用 bean:
Simply create a small Json utility that can do marshalling and unmarshalling and autowire your preconfigured ObjectMapper into it.只需创建一个小型 Json 实用程序,它可以进行编组和解组并将预配置的 ObjectMapper 自动装配到其中。
Harness Camels awesome Spring bean integration to call your utility and transform the Message in the route, eg:利用 Camels 很棒的 Spring bean 集成来调用您的实用程序并转换路由中的消息,例如:
from(uri) .unmarshal().json(JsonLibrary.Jackson) .beanRef("jsonUtil", "unmarshal") .bean(myBean);
I could not get any of the examples to work.我无法让任何示例工作。 A little disappointed that this is quite complicated from reading workarounds.有点失望的是,阅读变通方法非常复杂。
In my opinion camel should make it easy to use the Spring default object mapper by using the same Jackson bean that comes with the application.在我看来,camel 应该通过使用应用程序附带的相同 Jackson bean 来轻松使用 Spring 默认对象映射器。
I forgone the use of .json()
and swapped it for a processor.我放弃使用.json()
并将其换成处理器。
like the following, this used the objectMapper provided by Spring.如下所示,这里使用了 Spring 提供的 objectMapper。
Route路线
from(CONSUME_TAG)
.process("jsonProcessor")
.to("direct:anotherRoute")
.end();
Generic Processor notice how this Autowires the spring boot objectMapper bean.通用处理器注意这如何自动装配 spring boot objectMapper bean。
@Component
public class JsonProcessor implements Processor {
@Autowired
ObjectMapper objectMapper;
@Override
public void process(Exchange exchange) throws Exception {
exchange.getOut().setBody(objectMapper.writeValueAsString(exchange.getIn().getBody()));
}
}
Here is what works for me (Camel 2.2.0)这对我有用(Camel 2.2.0)
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.5</version>
</dependency>
REST configuration休息配置
restConfiguration().dataFormatProperty("moduleClassNames", "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule")
.dataFormatProperty("disableFeatures", "WRITE_DATES_AS_TIMESTAMPS")
Spring Boot configures ObjectMapper
with required jackson-datatype-jsr310
module during start up and puts it to the Spring Context. Spring Boot 在启动期间使用所需jackson-datatype-jsr310
模块配置ObjectMapper
,并将其放入 Spring 上下文。
New versions of Camel don't use ObjectMapper
form Spring Context by default and create own ObjectMapper
without jackson-datatype-jsr310
. Camel 的新版本默认不使用ObjectMapper
形式 Spring Context 并在没有jackson-datatype-jsr310
的情况下创建自己的ObjectMapper
。
application.yaml
对于骆驼的新版本,将此添加到application.yaml
中。yamlcamel.dataformat.jackson.auto-discover-object-mapper: true
from("some").unmarshal().json(SomeClass.class)
And don't use并且不要使用
from("some").unmarshal(new JacksonDataFormat(SomeClass.class))
It will not get ObjectMapper
from Spring Context.它不会从 Spring 上下文中获取ObjectMapper
。
To check what happens debug AbstractJacksonDataFormat.doStart()
method.要检查会发生什么调试AbstractJacksonDataFormat.doStart()
方法。 It is called during Camel start up and gets ObjectMapper
from the Spring Context or creates a new one.它在 Camel 启动期间被调用并从 Spring 上下文中获取ObjectMapper
或创建一个新的。 When new ObjectMapper
is created, it doesn't have required Jackson jackson-datatype-jsr310
module and an error happens.创建新的ObjectMapper
时,它不需要 Jackson jackson-datatype-jsr310
模块并发生错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.