GSON JsonObject 和 LinkedTreeMap
GSON JsonObject and LinkedTreeMap
我注意到没有一种简单的包罗万象(无 POJO)的方法来将(在 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
}
}
我很好奇为什么会出现这种情况 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);
}
我注意到没有一种简单的包罗万象(无 POJO)的方法来将(在 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
}
}
我很好奇为什么会出现这种情况 and/or 如果泛型方法不正确。
与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);
}