When Java generic erasure encounters JSON serialization and deserialization

When Java generic type erasure encounters JSON serialization and deserialization

preface

-Recently, I saw Spring's source code implementation of RestTemplate and had some thoughts. Suddenly, I thought that I had handled such a scene before. This time, I sorted out an article and watered it all at once, ha ha. Ask questions first:

  • If you were asked to convert complex JSON objects into Java beans, what would you write?
  • -If there are generics in the Java Bean to be converted, how to convert it?

This time, we focus on how to correctly tell the Java runtime what the specific type in the generic type is during JSON deserialization.

test

It comes from a scene encountered in the work. Using httpClient to call the interfaces of other services, you want to deserialize the JSON format string in the body of the Response into a Java Bean.

  • Java beans for testing:
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 + '\'' +
                '}';
    }
}
  • bronze
    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);
    }

Console printing:

TestBean{name='aaa', code='123'}
  • gold
    This time, add the attribute list < T > listbean in TestBean;
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", "attribute 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 + '\'' +
                '}';
    }
}

Console printing:

TestBean{name='aaa', code='123', listBean=[{"name":"attribute a","id":1}]}
  • Encountered a stumbling block
    Let's print the attribute name of the object in the listBean collection
System.out.println(testBean.getListBean().get(0).getName());

Console error:

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)

A class conversion exception is reported, indicating that JSONObject cannot be converted to a we just defined. Why?

  • Debug to see what's going on

It turns out that after JSONObject helps us deserialize, the collection is still JSONObject. Here we will talk about the type erasure of Java generics.

When deserializing to TestBean, JSONObject does not know what type is stored in the listBean collection, but it can still be converted successfully. This is the credit of generic type erasure. The type erasure mechanism of generics is convenient for us, but it also leaves us thinking when we deserialize. How can we know what the types in our generics are when we deserialize JSON?

  • King
        TestBean<A> result = jsonObject.toJavaObject(new TypeReference<TestBean<A>>() {});
        System.out.println(result);
        System.out.println(result.getListBean().get(0).getName());

Using TypeReference to specify the types in the generic type, the sequential deserialization is successful, and the objects in the collection are also converted.

Keywords: JSON

Added by mysoogal on Sun, 30 Jan 2022 09:14:47 +0200