JSON反序列化中的泛型问题及解决方法

1、问题

Java的泛型是编译期擦除,因此反序列化无法直接指定泛型类型:

List<User> list = JsonbBuilder.create().fromJson(json, List<User>.class);

编译不通过,改为List.class又达不到预期效果。

2、解决办法

jackson的做法:

List<User> list = JsonbBuilder.create().fromJson(json, new TypeReference<List<User>>(){});

注:Java 泛型擦除机制下,只有匿名子类能保留运行时泛型信息,预定义子类无法实现这一点。所以上面TypeReference后面的一对大括号不能省。
new TypeReference<>(){} 实际上是创建了一个TypeReference的 匿名子类。匿名子类的类定义会 保留父类的泛型参数信息(通过 getGenericSuperclass() 可获取)。
因此,如果提前预定义一个TypeReference的子类ConcreteType,再new ConcreteType<List>(),则会反序列化失败,因为只有匿名子类能保留运行时泛型信息。
以上机制,可以了解一下Java泛型的规范:

具体类的泛型参数在运行时不可见,只有匿名类或反射生成的类型能保留。

johnzon的做法:

List<User> list = JsonbBuilder.create().fromJson(json, new JohnzonCollectionType<List<User>>(){});

genson的做法:

List<User> list = JsonbBuilder.create().fromJson(json, new GenericType<List<User>>(){});

或者使用构造方法传入类型类和泛型类

List<User> list = JsonbBuilder.create().fromJson(json, new JohnzonParameterizedType(List.class, User.class));

注:User.class后面还可以接更多的泛型参数

总结

巧妙之处,Java虽然是泛型擦除,但子类可以拿到父类上的泛型类型。
所以TypeReferenceJohnzonCollectionTypeGenericType,无一例外都是抽象类,后面接了对大括号,表示是创建了该抽象类的匿名子类的实例,再由实例在运行时获取父类的泛型类型。

posted @ 2024-08-07 15:43  漠孤烟  阅读(424)  评论(0)    收藏  举报