当Java泛型类型擦除遇到JSON序列化和反序列化
前言
-最近看到了Spring 关于 RestTemplate的源码实现又有了一些思考,突然想到之前自己处理过这样的场景,这次整理一篇文章水一下子,哈哈哈哈。先抛出问题:
- 如果让你让对复杂JSON对象转为Java Bean你会怎么写?
- -要转换的Java Bean 中有泛型,又要怎么转?
本次重点我们放在JSON 反序列化时如何正确告诉Java 运行时,泛型中具体的类型是什么。
测试
源于工作中遇到的一个场景,使用httpClient 调用其他服务的接口,想要把Response
的body 中的JSON 格式的字符串反序列化为Java Bean。
- 测试用的Java Bean:
class TestBean {
private String name;
private String code;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String toString() {
return "TestBean{" +
"name='" + name + '\'' +
", code='" + code + '\'' +
'}';
}
}
- 青铜
public static void main(String[] args) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "aaa");
jsonObject.put("code", "123");
TestBean testBean = JSONObject.toJavaObject(jsonObject, TestBean.class);
System.out.println(testBean);
}
控制台打印:
TestBean{name='aaa', code='123'}
- 黄金
这次在TestBean 添加属性List<T> listBean;
public class Test1 {
public static void main(String[] args) {
/* JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "aaa");
jsonObject.put("code", "123");
TestBean testBean = JSONObject.toJavaObject(jsonObject, TestBean.class);
System.out.println(testBean);*/
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", "aaa");
jsonObject.put("code", "123");
JSONObject a = new JSONObject();
a.put("id", 1);
a.put("name", "属性a");
JSONArray array = new JSONArray();
array.add(a);
jsonObject.put("listBean", array);
TestBean<A> testBean = JSONObject.toJavaObject(jsonObject, TestBean.class);
System.out.println(testBean);
}
}
class TestBean<T> {
private String name;
private String code;
List<T> listBean;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public List<T> getListBean() {
return listBean;
}
public void setListBean(List<T> listBean) {
this.listBean = listBean;
}
@Override
public String toString() {
return "TestBean{" +
"name='" + name + '\'' +
", code='" + code + '\'' +
", listBean=" + listBean +
'}';
}
}
class A {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "A{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
控制台打印:
TestBean{name='aaa', code='123', listBean=[{"name":"属性a","id":1}]}
- 遇到了绊脚石
我们打印一下listBean
集合中对象的属性name
System.out.println(testBean.getListBean().get(0).getName());
控制台报错:
Exception in thread "main" java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to com.zhw.free.zhwfreedemo.testjson.A
at com.zhw.free.zhwfreedemo.testjson.Test1.main(Test1.java:30)
报了类转换异常,提示JSONObject 无法转换为我们刚刚定义的A,这是为什么呢?
- Debug看一下究竟
原来JSONObject帮我们反序列化后,集合中依然是JSONObject。这里就要说说Java 的泛型的类型擦除了。
在反序列化为TestBean的时候,JSONObject并不知道listBean集合中存放的类型是什么,但是依然可以转换成功。这就是泛型的类型擦除的功劳。泛型的类型擦除机制方便了我们,但也给我们反序列化时留下了思考,如何让JSON反序列化时知道我们泛型中的类型是什么呢?
- 王者
TestBean<A> result = jsonObject.toJavaObject(new TypeReference<TestBean<A>>() {});
System.out.println(result);
System.out.println(result.getListBean().get(0).getName());
利用TypeReference
指定泛型中的类型, 就可以顺利反序列化成功,并且 集合中的对象也转换过来了。