如何在 Spring Boot 中为 Camel 配置 Jackson ObjectMapper
How to configure Jackson ObjectMapper for Camel in Spring Boot
我正在尝试使用 Jackson 在 Camel 路由上将 POJO 序列化和反序列化到 JSON。其中一些具有 Java 8 个 LocalDate 字段,我希望将它们序列化为 YYYY-MM-DD 字符串,而不是整数数组。
我们只为 Spring 启动应用程序使用 Java 配置,因此没有 XML Camel 配置。
我已经成功创建了一个 ObjectMapper 来执行我想要的操作,通过将其添加到我们的依赖项中,我们系统的其他部分正在使用它:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
这是我们的应用程序配置:
@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
return builder
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.build();
}
传出 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()");
}
}
传入消息路由示例:
@Component
public class MyRouteBuilder extends RouteBuilder {
@Autowired
private MyBean myBean;
@Override
public void configure() {
from(uri)
.unmarshal().json(JsonLibrary.Jackson)
.bean(myBean);
}
}
但是,默认情况下,Camel 创建自己的 ObjectMapper 实例,因此不会使用 Jackson2ObjectMapperBuilder
自动添加的 JSR310 serializers/deserializers 或禁用的 WRITE_DATES_AS_TIMESTAMPS
功能。我已阅读 Camel JSON 文档,但它没有显示如何使用 Spring 配置添加自定义数据格式,或如何为所有类型应用全局自定义。
那么我如何告诉 Camel 使用我的 ObjectMapper,只使用 Spring 引导 Java 配置?
在 java 代码中创建 JacksonDataFormat
和 enable/disable 您想要的功能,然后在 Camel 路由中使用该实例。
.unmarshal(myInstanceGoesHere).
这是使用marshal
时的例子,同样可以与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");
}
});
如果 Camel 给你带来麻烦,我会恢复直接使用 beans:
只需创建一个小的 Json 实用程序,它可以进行编组和解组并将您预配置的 ObjectMapper 自动连接到其中。
利用 Camels 真棒 Spring bean 集成来调用您的实用程序并转换路由中的消息,例如:
from(uri)
.unmarshal().json(JsonLibrary.Jackson)
.beanRef("jsonUtil", "unmarshal")
.bean(myBean);
我通过单步执行 Camel 代码找到了解决方案。因此,虽然它可以满足我的要求,但它可能无法与未来版本的 Camel 一起使用,因为它似乎没有记录并且可能不受支持。
除了问题中的 ObjectMapper
bean 之外,我所做的就是将以下 bean 添加到我的 Spring 配置中:
@Bean(name = "json-jackson")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public JacksonDataFormat jacksonDataFormat(ObjectMapper objectMapper) {
return new JacksonDataFormat(objectMapper, HashMap.class);
}
重点注意事项:
JacksonDataFormat
没有采用没有解组类型的 ObjectMapper
的构造函数。但是,在默认构造函数中,如果未提供解组类型,则会使用 HashMap.class
,因此我使用它。神奇的是,这似乎随后习惯于解组所有 POJO 类型。如果您还需要其他 类 的更具体的数据格式,您也需要在其中设置 ObjectMapper
。
- Camel 似乎在 bean 注册表中搜索名为 "json-jackson" 的 bean,因此将 Spring bean 设置为使用该名称会诱使 Camel 不创建新的 bean 而是使用我的 bean。
- bean 范围必须设置为
SCOPE_PROTOTYPE
,因为 REST DSL 期望获得 DataFormat
的新实例。参见 CAMEL-7880。
使用 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>
并在 CamelContextConfiguration
class 中,自动装配 JacksonDataFormat
以配置 class 路径模块的发现和序列化选项的配置:
@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);
}
}
到目前为止,只有@david-edwards 的建议对我有用。我首先用 id 定义了一个数据格式 bean:"json-jackson"
<bean id="json-jackson" class="com.mydomain.JacksonDataFormatExt" />
然后格式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);
}
}
和 CustomEnumSerializer class:
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();
}
}
}
我设法使用 org.apache.camel:camel-jackson-starter:2.20.0
非常方便地为 Camel 配置了 ObjectMapper
它公开了一些有用的 ObjectMapper 属性,以便通过 Spring 应用程序属性进行配置。例如,WRITE_DATES_AS_TIMESTAMPS 可以直接从 application.yaml 或 application.properties 文件中设置。
查找 JacksonDataFormatConfiguration class 了解更多详细信息。
我还需要使用一些 Mixins,所以我仍然需要配置 Camel 以使用 Spring 的 ObjectMapper。我最终得到了这个:
配置bean:
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return new Jackson2ObjectMapperBuilderCustomizer() {
@Override
public void customize(Jackson2ObjectMapperBuilder builder) {
builder.mixIn(Person.class, PersonMixin.class);
}
}
}
application.yaml:
camel:
dataformat:
json-jackson:
disable-features: WRITE_DATES_AS_TIMESTAMPS
object-mapper: jacksonObjectMapper
其中jacksonObjectMapper
是配置的Jackson2ObjectMapperBuilder构建的ObjectMapper bean的名称
我无法使用任何示例。有点失望的是,阅读变通方法非常复杂。
在我看来,camel 应该通过使用应用程序附带的相同 Jackson bean 来简化 Spring 默认对象映射器的使用。
我放弃使用 .json()
并将其换成处理器。
如下图,使用了Spring提供的objectMapper。
路线
from(CONSUME_TAG)
.process("jsonProcessor")
.to("direct:anotherRoute")
.end();
通用处理器 注意这个 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()));
}
}
如果其他人想知道如何使用版本中的修复程序。 2.17..
我使用这个 xml 配置让它工作:
<camel:camelContext id="defaultCamelContext">
.....
<camel:dataFormats>
<camel:json id="json" library="Jackson" objectMapper="myObjectMapper"/>
</camel:dataFormats>
</camel:camelContext>
..其中 myObjectMapper 是类型为 ObjectMapper
的 spring bean 的名称
我通过在 pom 中包含 jackson 依赖项解决了问题
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson-starter</artifactId>
<version>${camel.version}</version>
</dependency>
现在,只需在路由配置中添加 JacksonDataFormat
public void configure() throws Exception {
JacksonDataFormat jsonDf = new JacksonDataFormat(Card.class);
jsonDf.setPrettyPrint(true);
from("direct:endpoint")
.marshal(jsonDf)
.convertBodyTo(String.class)
.....
}
各位好消息,Spring Boot 现在支持对象映射器自动发现!只需设置此 属性:
camel.dataformat.json-jackson.auto-discover-object-mapper=true
If set to true then Jackson will lookup for an objectMapper into the registry
日志:
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
(警告只是表示,您在 camel.dataformat.json-jackson.*
下的所有其他属性都将被忽略)
这对我有用
(骆驼 2.2.0)
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.5</version>
</dependency>
REST 配置
restConfiguration().dataFormatProperty("moduleClassNames", "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule")
.dataFormatProperty("disableFeatures", "WRITE_DATES_AS_TIMESTAMPS")
我正在尝试使用 Jackson 在 Camel 路由上将 POJO 序列化和反序列化到 JSON。其中一些具有 Java 8 个 LocalDate 字段,我希望将它们序列化为 YYYY-MM-DD 字符串,而不是整数数组。
我们只为 Spring 启动应用程序使用 Java 配置,因此没有 XML Camel 配置。
我已经成功创建了一个 ObjectMapper 来执行我想要的操作,通过将其添加到我们的依赖项中,我们系统的其他部分正在使用它:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
这是我们的应用程序配置:
@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
return builder
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.build();
}
传出 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()");
}
}
传入消息路由示例:
@Component
public class MyRouteBuilder extends RouteBuilder {
@Autowired
private MyBean myBean;
@Override
public void configure() {
from(uri)
.unmarshal().json(JsonLibrary.Jackson)
.bean(myBean);
}
}
但是,默认情况下,Camel 创建自己的 ObjectMapper 实例,因此不会使用 Jackson2ObjectMapperBuilder
自动添加的 JSR310 serializers/deserializers 或禁用的 WRITE_DATES_AS_TIMESTAMPS
功能。我已阅读 Camel JSON 文档,但它没有显示如何使用 Spring 配置添加自定义数据格式,或如何为所有类型应用全局自定义。
那么我如何告诉 Camel 使用我的 ObjectMapper,只使用 Spring 引导 Java 配置?
在 java 代码中创建 JacksonDataFormat
和 enable/disable 您想要的功能,然后在 Camel 路由中使用该实例。
.unmarshal(myInstanceGoesHere).
这是使用marshal
时的例子,同样可以与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");
}
});
如果 Camel 给你带来麻烦,我会恢复直接使用 beans:
只需创建一个小的 Json 实用程序,它可以进行编组和解组并将您预配置的 ObjectMapper 自动连接到其中。
利用 Camels 真棒 Spring bean 集成来调用您的实用程序并转换路由中的消息,例如:
from(uri) .unmarshal().json(JsonLibrary.Jackson) .beanRef("jsonUtil", "unmarshal") .bean(myBean);
我通过单步执行 Camel 代码找到了解决方案。因此,虽然它可以满足我的要求,但它可能无法与未来版本的 Camel 一起使用,因为它似乎没有记录并且可能不受支持。
除了问题中的 ObjectMapper
bean 之外,我所做的就是将以下 bean 添加到我的 Spring 配置中:
@Bean(name = "json-jackson")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public JacksonDataFormat jacksonDataFormat(ObjectMapper objectMapper) {
return new JacksonDataFormat(objectMapper, HashMap.class);
}
重点注意事项:
JacksonDataFormat
没有采用没有解组类型的ObjectMapper
的构造函数。但是,在默认构造函数中,如果未提供解组类型,则会使用HashMap.class
,因此我使用它。神奇的是,这似乎随后习惯于解组所有 POJO 类型。如果您还需要其他 类 的更具体的数据格式,您也需要在其中设置ObjectMapper
。- Camel 似乎在 bean 注册表中搜索名为 "json-jackson" 的 bean,因此将 Spring bean 设置为使用该名称会诱使 Camel 不创建新的 bean 而是使用我的 bean。
- bean 范围必须设置为
SCOPE_PROTOTYPE
,因为 REST DSL 期望获得DataFormat
的新实例。参见 CAMEL-7880。
使用 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>
并在 CamelContextConfiguration
class 中,自动装配 JacksonDataFormat
以配置 class 路径模块的发现和序列化选项的配置:
@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);
}
}
到目前为止,只有@david-edwards 的建议对我有用。我首先用 id 定义了一个数据格式 bean:"json-jackson"
<bean id="json-jackson" class="com.mydomain.JacksonDataFormatExt" />
然后格式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);
}
}
和 CustomEnumSerializer class:
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();
}
}
}
我设法使用 org.apache.camel:camel-jackson-starter:2.20.0
它公开了一些有用的 ObjectMapper 属性,以便通过 Spring 应用程序属性进行配置。例如,WRITE_DATES_AS_TIMESTAMPS 可以直接从 application.yaml 或 application.properties 文件中设置。
查找 JacksonDataFormatConfiguration class 了解更多详细信息。
我还需要使用一些 Mixins,所以我仍然需要配置 Camel 以使用 Spring 的 ObjectMapper。我最终得到了这个:
配置bean:
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return new Jackson2ObjectMapperBuilderCustomizer() {
@Override
public void customize(Jackson2ObjectMapperBuilder builder) {
builder.mixIn(Person.class, PersonMixin.class);
}
}
}
application.yaml:
camel:
dataformat:
json-jackson:
disable-features: WRITE_DATES_AS_TIMESTAMPS
object-mapper: jacksonObjectMapper
其中jacksonObjectMapper
是配置的Jackson2ObjectMapperBuilder构建的ObjectMapper bean的名称
我无法使用任何示例。有点失望的是,阅读变通方法非常复杂。
在我看来,camel 应该通过使用应用程序附带的相同 Jackson bean 来简化 Spring 默认对象映射器的使用。
我放弃使用 .json()
并将其换成处理器。
如下图,使用了Spring提供的objectMapper。
路线
from(CONSUME_TAG)
.process("jsonProcessor")
.to("direct:anotherRoute")
.end();
通用处理器 注意这个 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()));
}
}
如果其他人想知道如何使用版本中的修复程序。 2.17.. 我使用这个 xml 配置让它工作:
<camel:camelContext id="defaultCamelContext">
.....
<camel:dataFormats>
<camel:json id="json" library="Jackson" objectMapper="myObjectMapper"/>
</camel:dataFormats>
</camel:camelContext>
..其中 myObjectMapper 是类型为 ObjectMapper
的 spring bean 的名称我通过在 pom 中包含 jackson 依赖项解决了问题
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson-starter</artifactId>
<version>${camel.version}</version>
</dependency>
现在,只需在路由配置中添加 JacksonDataFormat
public void configure() throws Exception {
JacksonDataFormat jsonDf = new JacksonDataFormat(Card.class);
jsonDf.setPrettyPrint(true);
from("direct:endpoint")
.marshal(jsonDf)
.convertBodyTo(String.class)
.....
}
各位好消息,Spring Boot 现在支持对象映射器自动发现!只需设置此 属性:
camel.dataformat.json-jackson.auto-discover-object-mapper=true
If set to true then Jackson will lookup for an objectMapper into the registry
日志:
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
(警告只是表示,您在 camel.dataformat.json-jackson.*
下的所有其他属性都将被忽略)
这对我有用 (骆驼 2.2.0)
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.5</version>
</dependency>
REST 配置
restConfiguration().dataFormatProperty("moduleClassNames", "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule")
.dataFormatProperty("disableFeatures", "WRITE_DATES_AS_TIMESTAMPS")