如何使用Gson解析列表列表

时间:2017-02-16 15:30:03

标签: gson

我有一个看起来像这样的JSON:

{
     "1": [{"id": 1}, {"id": 2},{"id": 3}, {"id": 4}],
     "2": [{"id": 5}, {"id": 6}],
     "3": [{"id": 3}, {"id": 4}]
}

我希望将其解析为

List<List<Integer>> list

Map<Integer, List<Integer>> map

我应该如何使用Gson来实现这一目标?

1 个答案:

答案 0 :(得分:0)

你至少有两种选择:命令式和Gson式。

在命令行选项中,您可以使用Gson工具逐步解析给定JSON的整个JSON树:

final Gson gson = new GsonBuilder()
        // any custom Gson configuration here
        .create();
final Map<Integer, List<Integer>> map = new LinkedHashMap<>();
for ( final Entry<String, JsonElement> e : gson.fromJson(JSON, JsonElement.class).getAsJsonObject().entrySet() ) {
    // parseInt() ignores additional type adapters in Gson
    final Integer key = gson.fromJson(e.getKey(), Integer.class);
    final List<Integer> list = new ArrayList<>();
    @SuppressWarnings({ "unchecked", "rawtypes" })
    final Iterable<JsonObject> asJsonArray = (Iterable) e.getValue().getAsJsonArray();
    for ( final JsonObject el : asJsonArray ) {
        final Integer integer = gson.fromJson(el.get("id"), Integer.class);
        list.add(integer);
    }
    map.put(key, list);
}
out.println(map);
有点脏,不是吗?输出:

  

{1 = [1,2,3,4],2 = [5,6],3 = [3,4]}

另一种方法是制作一个自定义假DTO,它将成为Gson TypeToken的特殊标记,以便更精确地绑定JSON反序列化。例如:

abstract class FakeInt {

    private FakeInt() {
        throw new AssertionError("Not meant to be instantiated");
    }

}

这是一个抽象类,其中私有构造函数抛出错误,因此无法在外部实例化。永远不应该,它只是一个标记。

final class FakeIntDeserializer
        implements JsonDeserializer<Integer> {

    private static final JsonDeserializer<Integer> fakeIntDeserializer = new FakeIntDeserializer();

    private FakeIntDeserializer() {
    }

    static JsonDeserializer<Integer> getFakeIntDeserializer() {
        return fakeIntDeserializer;
    }

    @Override
    public Integer deserialize(final JsonElement json, final Type type, final JsonDeserializationContext context) {
        final JsonElement idElement = json.getAsJsonObject().get("id");
        return context.deserialize(idElement, Integer.class);
    }

}

这个反序列化器知道具有id属性的JSON数组元素:一旦从父对象获取,idElement被委托给下游解析器以提取Integer值。尽管可以使用idElement.getAsInt(),但委派idElement是为了尊重整个Gson配置(您可以对Integer类有特殊规则)。假冒DTO类及其JSON解串器如何协同工作:

final Type integerToFakeIntListType = new TypeToken<Map<Integer, List<FakeInt>>>() {
}.getType();
final Gson gson = new GsonBuilder()
        .registerTypeAdapter(FakeInt.class, getFakeIntDeserializer())
        .create();
final Map<Integer, List<Integer>> map = gson.fromJson(JSON, integerToFakeIntListType);
out.println(map);

请注意,Gson实例是使用某种黑客或作弊构建的:FakeInt类绑定到只返回Integer值的自定义反序列化程序或{{ 1}}。一旦绑定了类型及其反序列化器,就可以使用null实例,并且可以解析给定的JSON ,就像它是整数列表的整数映射一样。完全相同的输出如下:

  

{1 = [1,2,3,4],2 = [5,6],3 = [3,4]}