先来看一下Gson官方的阐述
Serializing and Deserializing Generic Types
When you call
toJson(obj)
, Gson callsobj.getClass()
to get information on the fields to serialize. Similarly, you can typically passMyClass.class
object in thefromJson(json, MyClass.class)
method. This works fine if the object is a non-generic type. However, if the object is of a generic type, then the Generic type information is lost because of Java Type Erasure. Here is an example illustrating the point:class Foo<T> { T value; } Gson gson = new Gson(); Foo<Bar> foo = new Foo<Bar>(); gson.toJson(foo); // May not serialize foo.value correctly gson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as Bar
大致意思就是:如果使用 new Gson().fromJson(json, Class<T>) 来反序列化会出现问题,导致JSON字符串无法反序列化为指定的泛型Bean。
究其原因就一个:Java代码编译后会擦除泛型,从而导致反序列化时,程序不清楚需要反序列化为什么类型的对象实例。
官方也给出了解决方案
The above code fails to interpret value as type Bar because Gson invokes
foo.getClass()
to get its class information, but this method returns a raw class,Foo.class
. This means that Gson has no way of knowing that this is an object of typeFoo<Bar>
, and not just plainFoo
.You can solve this problem by specifying the correct parameterized type for your generic type. You can do this by using the TypeToken class.
Type fooType = new TypeToken<Foo<Bar>>() {}.getType(); gson.toJson(foo, fooType); gson.fromJson(json, fooType);The idiom used to get
fooType
actually defines an anonymous local inner class containing a methodgetType()
that returns the fully parameterized type.
翻译一下大意:可以通过为泛型类型指定正确的参数化类型来解决此问题。您可以通过使用TypeToken类来实现这一点。
借助 TypeToken 这个类对象来弥补这一缺点。
本人所使用的的Gson版本是V2.9.0
官方教程有提到,使用示例:
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
其中:Foo<Bar>是指定的具体泛型类型。这样在反序列化时,不是直接放入泛型class:
gson.fromJson(json, clazz)
而是使用得到的Type类型传入
gson.fromJson(json, type)
type -> 为TypeToken后的Type类型
但教程点滞后,因为TypeToken 的构造方法均为protected,且为抽象类。官方提供的了已静态TypeToken.of(clazz) 方法,其内部有一个实现了该类的内部类SimpleTypeToken。
完整的使用示例:
// 以Foo class为例
Type type = TypeToken(Foo.class).getType();
Foo bean = new Gson().from(jsonStr, type);
接下来是针对List<T>的这种反序列化。在Java语法中,如果使用上述方式,语法上都校验不过,耿别谈反序列化了。
需要借助Gson内部的一些对象来实现。具体代码如下:
public static <T> List<T> fromJson(String json, Class<T> entityClazz) {
Gson gson = new Gson();
// 将数据先转为JsonArray
JsonArray list = gson.fromJson(json, JsonArray.class);
// 遍历再将每一个JsonElement反序列化为指定的Type,装进集合后返回
List<T> data = new LinkedList<>();
Type type = TypeToken.of(entityClazz).getType();
for (JsonElement object : list) {
data.add(gson.fromJson(object, type));
}
return data;
}
当然还有很多,这只是目前棘手的一点。有兴趣可自行研究,GitHub官网:https://github.com/google/gson/blob/master/UserGuide.md