1. 概述
Apache Camel 是一個強大的開源集成框架,它實現了許多已知的企業集成模式。
通常在通過 Camel 進行消息路由時,我們希望使用許多支持的插件式 數據格式。 鑑於 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. 結果領域類
在本文教程中,我們將使用幾個輕量級的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. 解析 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");
}
};
}在此示例中,我們使用名為 jsonInput 的直接端點。然後,我們調用 unmarshal 方法,該方法將我們接收到的 Camel 交換的消息主體序列化為指定的數據格式。
我們使用 JacksonDataFormat 類,自定義的 unmarshal 類型為FruitList. 這本質上是圍繞 Jackon ObjectMapper 的一個簡單包裝器,允許我們進行 JSON 序列化和反序列化。
最後,我們將 unmarshal 方法的結果發送到名為 marshalledObject 的模擬端點。 如你所見,這就是我們測試路由是否正確的方法。
考慮到這一點,讓我們編寫第一個單元測試:
@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 與 Spring Boot 的有用註解。
- 然後,我們使用 @EndpointInject 註解注入 MockEndpoint。此註解允許我們在不手動從 Camel 上下文中查找它們的情況下設置模擬端點。在這裏,我們將 mock:marshalledObject 設置為 Camel 端點的 URI。它指向一個名為 marshalledObject 的 mock 端點。
- 之後,我們設置測試期望。我們的 mock 端點應接收一條消息,且消息類型應為 FruitList。
- 現在,我們準備將 JSON 輸入文件作為 String 發送到我們之前定義的 direct 端點。我們使用 ProducerTemplate 類將消息發送到端點。
- 在檢查我們的 mock 期望是否已滿足後,我們可以檢索 FruitList 並檢查其內容是否符合預期。
此測試確認我們的路由正常工作,並且 JSON 按照預期進行反序列化。太棒了!
5. 解析 JSON 水果 數組
另一方面,我們可能不想使用容器對象來存儲我們的 水果 對象。我們可以修改 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 message routing 和 camel-jackson 組件。 這兩種方法的主要區別在於,JacksonDataFormat 會將數據反序列化為對象類型,而 ListJacksonDataFormat 則會將數據反序列化為列表類型。