1. 引言
使用 Jackson 處理預定義的 JSON 數據結構非常簡單。然而,有時我們需要處理動態的 JSON 對象,這些對象具有未知的屬性。
在本快速教程中,我們將學習多種將動態 JSON 對象映射到 Java 類的方法。
請注意,在所有測試中,我們假設我們擁有類型為 com.fasterxml.jackson.databind.ObjectMapper 的字段 objectMapper。
2. 使用 JsonNode
假設我們要處理在線商店中的產品規格。 所有產品都具有一些共同的屬性,但它們也具有不同的屬性,具體取決於產品的類型。
例如,我們想知道手機顯示器的寬高比,但這個屬性對於鞋子來説意義不大。
數據結構如下所示:
{
"name": "Pear yPhone 72",
"category": "cellphone",
"details": {
"displayAspectRatio": "97:3",
"audioConnector": "none"
}
}我們存儲動態屬性在 details 對象中。
我們可以通過以下 Java 類映射常見的屬性:
class Product {
String name;
String category;
// standard getters and setters
}此外,我們還需要對 details 對象進行適當的表示。例如,com.fasterxml.jackson.databind.JsonNode 可以處理動態鍵。
要使用它,我們需要將其作為字段添加到我們的 Product 類中:
class Product {
// common fields
JsonNode details;
// standard getters and setters
}最後,我們驗證它是否有效:
String json = "<json object>";
Product product = objectMapper.readValue(json, Product.class);
assertThat(product.getName()).isEqualTo("Pear yPhone 72");
assertThat(product.getDetails().get("audioConnector").asText()).isEqualTo("none");然而,這個解決方案存在問題:我們的類依賴於 Jackson 庫,因為我們有 JsonNode 字段。
3. 使用 Map
我們可以通過使用 java.util.Map 來解決這個問題,應用於 details 字段。更準確地説,我們需要使用 Map<String, Object>。
其他部分可以保持不變:
class Product {
// common fields
Map<String, Object> details;
// standard getters and setters
}然後我們可以用一個測試來驗證它:
String json = "<json object>";
Product product = objectMapper.readValue(json, Product.class);
assertThat(product.getName()).isEqualTo("Pear yPhone 72");
assertThat(product.getDetails().get("audioConnector")).isEqualTo("none");4. 使用 @JsonAnySetter
前述的解決方案在對象僅包含動態屬性時是良好的選擇。然而,有時我們會在單個 JSON 對象中混合使用固定和動態屬性。
例如,我們可能需要扁平化我們的產品表示:
{
"name": "Pear yPhone 72",
"category": "cellphone",
"displayAspectRatio": "97:3",
"audioConnector": "none"
}我們可以將這種結構視為一個動態對象。不幸的是,這意味着我們無法定義常見的屬性,而是必須動態地處理它們。
或者,我們可以使用 @JsonAnySetter 來標記一個用於處理額外的、未知的屬性的方法。 這樣的方法應該接受兩個參數:名稱和值。
class Product {
// common fields
Map<String, Object> details = new LinkedHashMap<>();
@JsonAnySetter
void setDetail(String key, Object value) {
details.put(key, value);
}
// standard getters and setters
}請注意,必須實例化 details 對象以避免 NullPointerException。
由於我們使用 Map 存儲動態屬性,我們可以以之前的方式使用它:
String json = "<json object>";
Product product = objectMapper.readValue(json, Product.class);
assertThat(product.getName()).isEqualTo("Pear yPhone 72");
assertThat(product.getDetails().get("audioConnector")).isEqualTo("none");5. 創建自定義反序列器
對於大多數情況,這些解決方案已經足夠好用;然而,有時我們需要更多的控制。例如,我們可以將 JSON 對象的反序列化信息存儲在數據庫中。
我們可以使用自定義反序列器來處理這些情況。由於這是一個更復雜的主題,因此我們在另一篇文章中進行了詳細説明,即《Jackson 自定義反序列化入門》。
6. 結論
在本文中,我們探討了多種處理動態 JSON 對象的方法,這些方法使用了 Jackson。