概述
在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上找到:链接。