1. 概述
Apache Camel 是一個功能強大的開源集成框架,它實現了許多已知的企業集成模式。
通常在通過 Camel 進行消息路由時,我們希望使用許多支持的插件式 數據格式。 鑑於 JSON 在許多現代 API 和數據服務中很受歡迎,因此它成為一種常見的選擇。
在本教程中,我們將探討幾種將 JSON 數組解析為 Java 對象列表的方法,使用 camel-jackson 組件。
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. 結果域類
在整個教程中,我們將使用幾個輕量級POJO對象來建模我們的結果域。讓我們定義一個具有id和name的類來表示一個結果:
public class Fruit {
private String name;
private int id;
// standard getter and setters
}
接下來,我們將定義一個包含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. Unmarshalling a JSON FruitList
在第一個示例中,我們將一個簡單的水果列表表示為 JSON 格式:
{
"fruits": [
{
"id": 100,
"name": "Banana"
},
{
"id": 101,
"name": "Apple"
}
]
}
最重要的是,我們應該強調的是,這個 JSON 代表了一個包含一個名為 fruits 的屬性的對象
現在,讓我們為執行反序列化設置我們的 Apache Camel 路由:
@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 方法,該方法將接收到的每個消息主體(來自 Camel 交換)反序列化為指定的格式。
我們正在使用 JacksonDataFormat 類,自定義反序列化類型為 FruitList
這本質上是一個圍繞 Jackon ObjectMapper 的簡單包裝器,並且允許我們向和從 JSON 中進行序列化。
最後,我們將 unmarshal 方法的結果發送到名為 marshalledObject 的 mock 端點。正如我們即將看到的,這正是我們用來測試路由是否正常工作的方式。
讓我們來走過我們測試中的關鍵部分,以瞭解發生了什麼:
- 首先,我們首先添加了 @CamelSpringBootTest 註解,這是一個有用的註解,用於使用 Spring Boot 測試 Camel。
- 然後,我們使用 @EndpointInject 註解注入 MockEndpoint。此註解允許我們在不手動從 Camel 上下文中查找它們的情況下設置 mock 端點。在此處,我們將 mock:marshalledObject 設置為 Camel 端點的 URI。它指向一個名為 marshalledObject 的 mock 端點。它指向一個名為 marshalledObject 的 mock 端點。
- 之後,我們設置測試期望。我們的 mock 端點應接收一條消息,並且消息類型應為 FruitList。
- 現在我們準備好將 JSON 輸入文件作為 String 發送到 direct 端點。我們使用 ProducerTemplate 類將消息發送到端點。
- 在檢查我們的 mock 期望是否已滿足後,我們可以檢索 FruitList 並檢查其內容是否符合預期。
此測試確認我們的路由正在正常工作,並且我們的 JSON 正在被反序列化為預期的格式。太棒了!
5. 將JSON Fruit數組反序列化
另一方面,我們可能不想使用容器對象來存儲我們的Fruit對象。我們可以修改我們的JSON以直接存儲一個水果數組:
[
{
"id": 100,
"name": "Banana"
},
{
"id": 101,
"name": "Apple"
}
]
這次,我們的路由幾乎與之前相同,但我們將其設置成專門處理JSON數組:
@Bean
RoutesBuilder route() {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
from("direct:jsonInput").unmarshal(new ListJacksonDataFormat(Fruit.class))
.to("mock:marshalledObject");
}
};
}
正如我們所見,與之前示例唯一的區別是我們正在使用ListJacksonDataFormat類,並使用自定義的反序列化類型Fruit。這是一種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());
}
然而,與上一節中看到的測試相比,有兩個微妙的差異:
- 我們首先設置mock期望包含一個帶有List.class正文的期望。
- 當我們以List.class的形式檢索消息正文時,我們會收到有關類型安全性的標準警告——因此使用了@SuppressWarnings(“unchecked”).
6. 結論
在本文中,我們看到了兩種簡單的JSON數組反序列化方法,即使用camel消息路由和camel-jackson組件。這兩種方法的主要區別在於,JacksonDataFormat反序列化到對象類型,而ListJacksonDataFormat則反序列化到列表類型。