概述

在Jackson中处理预定义的JSON数据结构相对直接。然而,有时我们需要处理动态JSON对象,这些对象具有未知属性。本快速教程将介绍如何将动态JSON对象映射到Java类的多种方法。

请注意,在所有示例测试中,我们假设有一个ObjectMapper类型的字段。

1. 引言

假设我们要在电子商务网站上处理产品规格。所有产品都有一些共同属性,但根据产品的类型,它们还有不同的属性。例如,我们想知道手机显示屏的比例,但对于鞋子来说,这个属性就不太有意义。

数据结构如下:

{
    "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字段。

2. 使用JsonNode

通过使用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");

3. 使用@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");

4. 创建自定义反序列化器

对于大多数情况,这些解决方案已经足够;但在某些情况下,我们可能需要更多的控制。例如,我们可能想在数据库中存储关于JSON对象的反序列化信息。

我们可以针对这种情况使用自定义反序列化器。由于这是一个更复杂的话题,我们将在另一篇文章《Jackson中的自定义反序列化入门》中详细介绍:链接

5. 总结

在这篇文章中,我们讨论了使用Jackson处理动态JSON对象的多种方法。如往常一样,示例代码可在GitHub上找到:链接


« 上一篇: Java Weekly, 第261期