Java 中对于 JSON 数据的对象化, Jackson 方便好用,还有 jsonschema2pojo 等工具帮忙生成 Java 类。 然而,有时对于嵌套的 JSON, 我们只关心其中的一两项, 甚至有时希望将内部字段其合并到外部的对象中。
可惜的是,现在 Jackson 还没有支持 @JsonWrapped
注解: Issues (只有 @JsonUnwrapped
用于生成 JSON)。 所以通常的解决方案是老老实实一层层写对应的类。
为了避免写一堆处理嵌套的类, 这里给出另外的两种解决方案。
考虑下面这个 JSON 数据:
{
"results": {
"results_returned": "10",
"shop": [
{
"name": "Just a name",
"photo": {
"l": "https://example.com/1.jpg",
"s": "https://example.com/2.jpg"
},
"budget": {
"average": "100",
"name": "AVG100",
"code": "A03"
}
}
]
}
}
对外层的 results , 可用 @JsonRootName("results")
处理掉:
@JsonRootName("results")
public class ShopResults {
@JsonProperty("results_returned")
public int ResultsReturned;
@JsonProperty("shop")
public List<Shop> Shops;
}
然后给 Shop 类添加注解:
@JsonIgnoreProperties(ignoreUnknown = true)
public class Shop {
@JsonProperty("name")
public String Name;
@JsonIgnore
public String Average = "";
@JsonProperty("budget")
private void unpackAverageFromBudget(Map<String, String> budget) {
Average = budget.get("average");
}
@JsonIgnore
public String getPhotoL() {
String photoL= "";
try {
@SuppressWarnings("unchecked")
Map<String, String> photoMap = (Map<String, String>)additionalProperties.get("photo");
photoL = photoMap.get("l");
} catch (Exception e) {
e.printStackTrace();
}
return photoL;
}
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
大功告成, 并不需要自定义 JsonSerializer
。
简单解释一下。对于 photo 和 budget 嵌套字段,我们采用了两种不同的解决方案:
第一种,通过 @JsonProperty
注解一个方法,这个方法的参数是 Map<String, String>(Map<String, Object>)
,Jackson
会自动调用这个方法,所以我们在方法中获取并设置到想要的字段即可。注意适当添加 @JsonIgnore
防止 Jackson 的自动处理。
第二种,通过 @JsonAnyGetter
和 @JsonAnySetter
, 将其他字段通通都处理为 Map 并保存, 然后通过自定义的 getter 方法,从 Map 中获取想要的字段即可。
最后吐槽一句:iOS 上的 ObjectMapper 已经支持 Path 的写法了: average <- map["budget.average"]
。