1. 概述
使用JSON時的一個典型用例是從一個模型轉換到另一個模型。例如,我們可能希望將複雜的、密集嵌套的對象圖轉換為用於另一個領域的更簡單模型。
在本快速教程中,我們將探討如何使用Jackson將嵌套值映射出來,從而扁平化複雜的結構。我們將以三種不同的方式反序列化JSON:
- >使用@JsonProperty
- >使用JsonNode
- >使用自定義JsonDeserializer
2. Maven 依賴
首先,添加以下依賴到 pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
可以在 Maven Central 找到 jackson-databind 的最新版本。
3. JSON 來源
請考慮以下 JSON 作為我們示例的原始材料。
雖然結構是人為設計的,但請注意,我們包含兩個層級的嵌套屬性:
{
"id": "957c43f2-fa2e-42f9-bf75-6e3d5bb6960a",
"name": "The Best Product",
"brand": {
"id": "9bcd817d-0141-42e6-8f04-e5aaab0980b6",
"name": "ACME Products",
"owner": {
"id": "b21a80b1-0c09-4be3-9ebd-ea3653511c13",
"name": "Ultimate Corp, Inc."
}
}
}
4. 簡化領域模型
在以下描述的 產品 類中定義的扁平化領域模型中,我們將提取 品牌名稱,該名稱位於源 JSON 中的嵌套級別為一級的內部。
此外,我們將提取 所有者名稱,該名稱位於嵌套級別為二級的嵌套 品牌 對象內部:
public class Product {
private String id;
private String name;
private String brandName;
private String ownerName;
// standard getters and setters
}
5. 使用註釋進行映射
為了映射嵌套的brandName屬性,我們首先需要將嵌套的brand對象解包為Map並提取name屬性。為了映射ownerName,我們首先將嵌套的owner對象解包為Map並提取其name屬性。我們可以指示Jackson通過使用@JsonProperty和我們在Product類中添加的自定義邏輯來解包嵌套屬性:
public class Product {
// ...
@SuppressWarnings("unchecked")
@JsonProperty("brand")
private void unpackNested(Map<String,Object> brand) {
this.brandName = (String)brand.get("name");
Map<String,String> owner = (Map<String,String>)brand.get("owner");
this.ownerName = owner.get("name");
}
}
我們的客户端代碼現在可以使用ObjectMapper來轉換我們的源JSON,該JSON存在於測試類內的SOURCE_JSON常量中:
@Test
public void whenUsingAnnotations_thenOk() throws IOException {
Product product = new ObjectMapper()
.readerFor(Product.class)
.readValue(SOURCE_JSON);
assertEquals(product.getName(), "The Best Product");
assertEquals(product.getBrandName(), "ACME Products");
assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}
6. 使用 JsonNode 進行映射
使用 JsonNode 映射嵌套的數據結構需要多做一些工作。在這裏,我們使用 ObjectMapper 的 readTree 來提取所需字段:
@Test
public void whenUsingJsonNode_thenOk() throws IOException {
JsonNode productNode = new ObjectMapper().readTree(SOURCE_JSON);
Product product = new Product();
product.setId(productNode.get("id").textValue());
product.setName(productNode.get("name").textValue());
product.setBrandName(productNode.get("brand")
.get("name").textValue());
product.setOwnerName(productNode.get("brand")
.get("owner").get("name").textValue());
assertEquals(product.getName(), "The Best Product");
assertEquals(product.getBrandName(), "ACME Products");
assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}
7. 映射與自定義 JsonDeserializer
使用自定義
首先創建
public class ProductDeserializer extends StdDeserializer<Product> {
public ProductDeserializer() {
this(null);
}
public ProductDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Product deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode productNode = jp.getCodec().readTree(jp);
Product product = new Product();
product.setId(productNode.get("id").textValue());
product.setName(productNode.get("name").textValue());
product.setBrandName(productNode.get("brand")
.get("name").textValue());
product.setOwnerName(productNode.get("brand").get("owner")
.get("name").textValue());
return product;
}
}
7.1. 手動註冊序列化器
要手動註冊我們的自定義序列化器,我們的客户端代碼必須將
@Test
public void whenUsingDeserializerManuallyRegistered_thenOk()
throws IOException {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Product.class, new ProductDeserializer());
mapper.registerModule(module);
Product product = mapper.readValue(SOURCE_JSON, Product.class);
assertEquals(product.getName(), "The Best Product");
assertEquals(product.getBrandName(), "ACME Products");
assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}
7.2. 自動註冊序列化器
作為自定義
@JsonDeserialize(using = ProductDeserializer.class)
public class Product {
// ...
}
採用這種方法,無需手動註冊。
讓我們看看客户端代碼使用自動註冊的方法:
@Test
public void whenUsingDeserializerAutoRegistered_thenOk()
throws IOException {
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(SOURCE_JSON, Product.class);
assertEquals(product.getName(), "The Best Product");
assertEquals(product.getBrandName(), "ACME Products");
assertEquals(product.getOwnerName(), "Ultimate Corp, Inc.");
}
8. 結論
在本文中,我們演示了使用 Jackson 解析包含嵌套值的 JSON 的多種方法。請參閲我們的主 Jackson 教程頁面以獲取更多示例。