1. 概述
Apache Camel 是一个强大的开源集成框架,支持多种已知的 企业集成模式(Enterprise Integration Patterns)。
在使用 Camel 进行消息路由时,通常我们会使用它提供的可插拔 数据格式(data formats)。由于 JSON 在现代 API 和数据服务中非常流行,因此它自然也成为了常用选择。
在本教程中,我们将介绍两种方式,使用 camel-jackson 组件将 JSON 数组 反序列化为 Java 对象列表。
2. 依赖配置
首先,将 camel-jackson-starter 添加到你的 pom.xml 中:
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jackson-starter</artifactId>
<version>3.21.0</version>
</dependency>
然后,为了支持单元测试,添加 camel-test-spring-junit5:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring-junit5</artifactId>
<version>3.21.0</version>
<scope>test</scope>
</dependency>
3. Fruit 领域类
本教程中,我们使用一些简单的 POJO 来建模水果领域。
先定义一个包含 id 和 name 的 Fruit
类:
public class Fruit {
private String name;
private int id;
// 标准 getter 和 setter
}
再定义一个容器类 FruitList
来包装 Fruit
列表:
public class FruitList {
private List<Fruit> fruits;
public List<Fruit> getFruits() {
return fruits;
}
public void setFruits(List<Fruit> fruits) {
this.fruits = fruits;
}
}
在接下来的章节中,我们将演示如何将一个表示水果列表的 JSON 字符串反序列化为这些领域类。最终目标是得到一个 List<Fruit>
类型的变量,便于后续处理。
4. 反序列化 JSON FruitList
在这个例子中,我们使用如下 JSON 格式来表示一个水果列表:
{
"fruits": [
{
"id": 100,
"name": "Banana"
},
{
"id": 101,
"name": "Apple"
}
]
}
⚠️ 注意:这个 JSON 表示的是一个包含名为 fruits 属性的对象,该属性的值才是我们真正要的数组。
接下来我们配置 Apache Camel 的 路由(route) 来执行反序列化操作:
@Bean
RoutesBuilder route() {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
from("direct:jsonInput").unmarshal(new JacksonDataFormat(FruitList.class))
.to("mock:marshalledObject");
}
};
}
解释一下:
- 使用
direct
接口,命名为jsonInput
- 调用
unmarshal
方法,使用JacksonDataFormat
并指定目标类型为FruitList.class
- 最终将结果发送到
mock:marshalledObject
接口用于测试
✅ JacksonDataFormat
是对 Jackson 的 ObjectMapper 的简单封装,支持 JSON 与 Java 对象之间的序列化和反序列化。
下面是我们为该路由编写的第一个单元测试:
@CamelSpringBootTest
@SpringBootTest
public class FruitListJacksonUnmarshalUnitTest {
@Autowired
private ProducerTemplate template;
@EndpointInject("mock:marshalledObject")
private MockEndpoint mock;
@Test
public void givenJsonFruitList_whenUnmarshalled_thenSuccess() throws Exception {
mock.setExpectedMessageCount(1);
mock.message(0).body().isInstanceOf(FruitList.class);
String json = readJsonFromFile("/json/fruit-list.json");
template.sendBody("direct:jsonInput", json);
mock.assertIsSatisfied();
FruitList fruitList = mock.getReceivedExchanges().get(0).getIn().getBody(FruitList.class);
assertNotNull("Fruit lists should not be null", fruitList);
List<Fruit> fruits = fruitList.getFruits();
assertEquals("There should be two fruits", 2, fruits.size());
Fruit fruit = fruits.get(0);
assertEquals("Fruit name", "Banana", fruit.getName());
assertEquals("Fruit id", 100, fruit.getId());
fruit = fruits.get(1);
assertEquals("Fruit name", "Apple", fruit.getName());
assertEquals("Fruit id", 101, fruit.getId());
}
}
测试的关键点包括:
- 使用
@CamelSpringBootTest
注解启动 Camel 测试环境 - 通过
@EndpointInject
注入mock
接口 - 设置期望:收到一条消息,且消息体是
FruitList
类型 - 使用
ProducerTemplate
将 JSON 字符串发送到direct:jsonInput
- 验证结果并断言内容正确
✅ 测试通过说明路由配置正确,JSON 被成功反序列化!
5. 反序列化 JSON Fruit 数组
有时候我们可能不想使用容器类,而是直接处理 JSON 数组:
[
{
"id": 100,
"name": "Banana"
},
{
"id": 101,
"name": "Apple"
}
]
对应的 Camel 路由配置几乎一样,只是换成了 ListJacksonDataFormat
:
@Bean
RoutesBuilder route() {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
from("direct:jsonInput").unmarshal(new ListJacksonDataFormat(Fruit.class))
.to("mock:marshalledObject");
}
};
}
✅ ListJacksonDataFormat
是专门用于处理 JSON 数组的 Jackson 数据格式类。
单元测试也很类似:
@Test
public void givenJsonFruitArray_whenUnmarshalled_thenSuccess() throws Exception {
mock.setExpectedMessageCount(1);
mock.message(0).body().isInstanceOf(List.class);
String json = readJsonFromFile("/json/fruit-array.json");
template.sendBody("direct:jsonInput", json);
mock.assertIsSatisfied();
@SuppressWarnings("unchecked")
List<Fruit> fruitList = mock.getReceivedExchanges().get(0).getIn().getBody(List.class);
assertNotNull("Fruit lists should not be null", fruitList);
assertEquals("There should be two fruits", 2, fruitList.size());
Fruit fruit = fruitList.get(0);
assertEquals("Fruit name", "Banana", fruit.getName());
assertEquals("Fruit id", 100, fruit.getId());
fruit = fruitList.get(1);
assertEquals("Fruit name", "Apple", fruit.getName());
assertEquals("Fruit id", 101, fruit.getId());
}
⚠️ 与前一个测试相比,有两点小变化:
- 期望消息体是
List.class
- 由于泛型擦除,获取
List<Fruit>
时会提示类型不安全,需要使用@SuppressWarnings("unchecked")
6. 总结
在本教程中,我们展示了使用 Camel 消息路由和 camel-jackson 组件反序列化 JSON 数组的两种方式:
- ✅
JacksonDataFormat
:用于将 JSON 反序列化为一个对象(如FruitList
) - ✅
ListJacksonDataFormat
:用于将 JSON 数组直接反序列化为List<Fruit>
类型
两者主要区别在于目标类型是对象还是列表。
📌 本文完整代码可从 GitHub 获取。