我注意到在GSON中没有一种简单的方法可以将JsonArray
转换为List
。我找到了一个使用TypeToken
的例子,当我试图将其移动到泛型方法时(我对Java泛型有点模糊),我发现了一些奇怪的行为,其中JsonObject
和LinkedTreeMap
被混淆了。参见下面的例子:
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
}
}
我只是好奇为什么会发生这种情况和/或泛型方法是否不正确。
1条答案
按热度按时间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
中使用它的方法: