问题 如何在Spring Boot中为Camel配置Jackson ObjectMapper


我正在尝试使用Jackson在Camel路由上对JSON进行序列化和反序列化。其中一些有Java 8 LocalDate字段,我希望它们被序列化为YYYY-MM-DD字符串,而不是整数数组。

我们只对我们的Spring Boot应用程序使用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实例,因此不会选择JSR310序列化程序/反序列化程序 Jackson2ObjectMapperBuilder 自动添加或禁用 WRITE_DATES_AS_TIMESTAMPS 特征。我看过了 骆驼JSON 文档,但它没有说明如何使用Spring配置添加自定义DataFormat,或者如何为所有类型应用全局自定义。

那么如何使用Spring Boot Java配置告诉Camel使用我的ObjectMapper?


5939
2017-10-28 17:11


起源

查看jackson的启用/禁用功能: camel.apache.org/json.html - Claus Ibsen
请分享你的骆驼路线。 - Fritz Duchardt
@ClausIbsen我读过这篇文章,但它没有说明如何让Camel知道自定义的DataFormat。或者如何使它适用于所有POJO而不是特定的类。 - David Edwards
@FritzDuchardt我现在已经添加了问题的示例路由。 - David Edwards


答案:


我已经通过单步执行Camel代码找到了解决方案。因此,虽然它可以满足我的需求,但它可能不适用于未来的Camel版本,因为它似乎没有文档记录且可能不受支持。

我所做的就是将以下bean添加到我的Spring配置中,除了我的 ObjectMapper bean中的问题:

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

需要注意的关键点:

  • 没有构造函数 JacksonDataFormat 需要一个 ObjectMapper 没有unmarshal类型。但是,在默认构造函数中 HashMap.class 当没有提供unmarshal类型时使用,所以我使用它。通过一些魔术,这似乎习惯于解组所有POJO类型。如果您还需要其他类的更具体的数据格式,则需要设置 ObjectMapper 在他们中间。
  • Camel似乎在bean注册表中搜索一个名为“json-jackson”的bean,因此设置Spring bean使用该名称会欺骗Camel而不是创建一个新的并使用我的。
  • bean范围必须设置为 SCOPE_PROTOTYPE 因为REST DSL期望得到一个新的实例 DataFormat。看到 CAMEL-7880

5
2017-10-30 09:21



到目前为止,这是唯一对我有用的东西。我的解决方案的一些变化:不需要objectMapper,我还创建了一个名为=“json-jackson”的bean,它是JacksonDataFormat的扩展。 - Georgios Stathis
只有对我有用的选项(使用Camel 2.17)! - jfneis


创建 JacksonDataFormat 在java代码中启用/禁用所需的功能,然后在Camel路由中使用该实例。

 .unmarshal(myInstanceGoesHere).

3
2017-10-29 07:17



谢谢。这是部分解决方案;我看到了许多不足之处:1。当使用restConfiguration()时,setJsonDataFormat()接受一个String,但我认为没有办法给JacksonDataFormat一个名字。每个rest()端点是否需要添加unmarshal()? 2.为每条路线添加自定义JacksonDataFormat效率低下,可能会被遗忘。我想全局配置一次。 3.我已经按照我想要的方式配置了ObjectMapper。配置一个新的似乎效率低下。它不仅仅是禁用功能;我也需要注册JSR310串行器/解串器。 - David Edwards
我记录了一个票证,允许配置一个自定义对象映射器实例用于数据格式: issues.apache.org/jira/browse/CAMEL-9275 - Claus Ibsen
谢谢,克劳斯。在我看来,它应该自动使用现有的ObjectMapper bean(如果有的话),并且只创建一个新的ObjectMapper(如果不存在)。 - David Edwards
@ClausIbsen我看到您在Camel 2.17中修复了该票证,但我们如何使用该修复程序?特别是在我们不使用Spring的OSGI蓝图环境中 - 我们只是通过ObjectMapper类型的蓝图(osgi-config.xml)创建一个bean,它会自动拾取它吗? - Michael Lucas
我尝试在JsonDataFormat中使用setModuleClassNames启用JavaTimeModule,但似乎不起作用。不幸的是,这不是启用/禁用功能的问题,所以我无法看到这个答案如何解决这个问题。 - jfneis


使用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 上课,自动装配 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);
    }
}

2
2017-12-22 13:04





我设法使用非常方便地为Camel配置ObjectMapper org.apache.camel:camel-jackson-starter:2.20.0

它通过Spring应用程序属性公开了一些有用的ObjectMapper属性。例如,可以从application.yaml或application.properties文件直接设置WRITE_DATES_AS_TIMESTAMPS。

查找JacksonDataFormatConfiguration类以获取更多详细信息。

我还需要使用一些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的名称


2
2017-10-18 12:50





到目前为止,只有@ david-edwards的建议对我有用。我首先定义了一个id为“json-jackson”的数据格式bean

<bean id="json-jackson" class="com.mydomain.JacksonDataFormatExt" />

然后是格式类:

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类:

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();
        }
    }
}

1
2018-04-15 01:30





如果有人想知道如何使用版本中的修复程序。 2.17 .. 我使用这个xml配置工作了:

 <camel:camelContext id="defaultCamelContext">
       .....
        <camel:dataFormats>
            <camel:json id="json" library="Jackson"  objectMapper="myObjectMapper"/>
        </camel:dataFormats>

 </camel:camelContext>

..where myObjectMapper是ObjectMapper类型的spring bean的名称


1
2018-03-23 17:30



在随附的camel XML示例中,您是否还可以包含<restConfiguration>元素?当我使用restConfiguration“<restConfiguration ... bindingMode =”json“jsonDataFormat =”json“/>”时,我收到一条错误消息“因为找不到JSon DataFormat json” - JohnC


如果Camel在那里给你带来麻烦,我会直接使用bean:

  1. 只需创建一个可以进行编组和解组的小型Json实用程序,并将预配置的ObjectMapper自动装入其中。

  2. Harness Camels非常棒的Spring bean集成来调用你的实用程序并在路由中转换Message,例如:

         from(uri)
            .unmarshal().json(JsonLibrary.Jackson)
            .beanRef("jsonUtil", "unmarshal")
            .bean(myBean);
    

0
2017-10-29 17:11





我无法让任何一个例子起作用。有点失望的是,阅读变通方法非常复杂。

在我看来,camel应该通过使用应用程序附带的相同Jackson bean来轻松使用Spring默认对象映射器。

我放弃了使用 .json() 并将其换成处理器。

如下所示,这使用了Spring提供的objectMapper。

路线

from(CONSUME_TAG)
 .process("jsonProcessor")
 .to("direct:anotherRoute")
 .end();

通用处理器  注意这是如何自动启动spring引导的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()));
    }

}

0
2018-03-23 13:41