GSON JsonObject 和 LinkedTreeMap

GSON JsonObject and LinkedTreeMap

我注意到没有一种简单的包罗万象(无 POJO)的方法来将(在 GSON 中)JsonArray 转换为 List。我找到了一个使用 TypeToken 的示例,当我尝试将其移动到通用方法时(我对 Java 泛型有点模糊),我发现了一些奇怪的行为,其中 JsonObjectLinkedTreeMap 感到困惑。请参见下面的示例:

public <T> List<T> jsonArrayToList(JsonArray array, Class<T> clazz) {
    Gson GSON = new Gson();
    Type listType = new TypeToken<List<T>>() {}.getType();
    return GSON.fromJson(array,listType);
}

@Test
public void testGson() {
    Gson GSON = new Gson();
    String jsonString = "[ {key:\"foo\",value:\"bar\"}, {key:\"shoe\",value:\"car\"} ]";
    JsonArray testArray = new JsonParser().parse(jsonString).getAsJsonArray();
    Type listType = new TypeToken<List<JsonObject>>() {}.getType();
    List<JsonObject> goodList = GSON.fromJson(testArray,listType);
    List<JsonObject> badList = jsonArrayToList(testArray, JsonObject.class);    // Actually a list of LinkedTreeMap?

    System.out.println( goodList );     // [{"key":"foo","value":"bar"}, {"key":"shoe","value":"car"}]
    System.out.println( badList );      // [{key=foo, value=bar}, {key=shoe, value=car}]

    try {
        long shoeCount = goodList.stream().filter(o -> o.get("key").getAsString().startsWith("sh")).count();
        System.out.println( shoeCount );    // 1
    } catch (Exception e) { System.out.println( e.getMessage() ); }

    try {
        long shoeCount = badList.stream().filter(o -> o.get("key").getAsString().startsWith("sh")).count();
        System.out.println( shoeCount );
    } catch (Exception e) {
        System.out.println(e.getMessage()); // com.google.gson.internal.LinkedTreeMap cannot be cast to com.google.gson.JsonObject
    }
}

我很好奇为什么会出现这种情况 and/or 如果泛型方法不正确。

一样,由于Java类型擦除,TypeToken<List<T>>在运行时实际上是一个TypeToken<List<Object>>,不管T有什么值。当 Gson 被告知反序列化为 Object 时,它使用对应于 JSON 数据的 Java 类型,对于你的 JSON 对象,即 java.util.Map (使用 Gson 的实现LinkedTreeMap).

因此,在创建 TypeToken 时,通常不应使用任何类型变量(不幸的是,Gson 本身不会阻止这种用法)。

对于您的特定用例,您可以使用方法 TypeToken.getParameterized。有了它,您可以创建具有在运行时指定的类型参数的参数化类型。这是以编译时缺少类型安全为代价的,因此您必须确保根据泛型类型向 getParameterized 提供正确数量的参数(例如 Map<K, V> 需要两个类型参数)和他们不验证类型变量的类型范围。

以下是如何在您的方法中使用它 jsonArrayToList:

public <T> List<T> jsonArrayToList(JsonArray array, Class<T> clazz) {
    Gson GSON = new Gson();
    Type listType = TypeToken.getParameterized​(List.class, clazz).getType();
    return GSON.fromJson(array,listType);
}