Jackson处理嵌套字段

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"]

comments powered by Disqus