GSON JSON对象和链接树Map

svdrlsy4  于 2022-11-06  发布在  其他
关注(0)|答案(1)|浏览(248)

我注意到在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
    }
}

我只是好奇为什么会发生这种情况和/或泛型方法是否不正确。

u3r8eeie

u3r8eeie1#

正如注解中所解释的,由于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);
}

相关问题