Bootstrap

当Java泛型擦除遇到JSON序列化和反序列化

当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看一下究竟
    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 指定泛型中的类型, 就可以顺利反序列化成功,并且 集合中的对象也转换过来了。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;