【java基础】根据泛型动态构造jackson的TypeReference(json反序列化为带泛型的类的对象)

根据泛型动态构造jackson的TypeReference

  • 引出问题
  • 使用TypeReference反序列化的例子
  • 根据泛型动态构造TypeReference
    • 带泛型的类如何表示?
    • 完成HttpClient的实现

引出问题

  1. 将json字符串反序列化为带泛型的类的对象怎么操作?
  2. 怎么动态构造`TypeReference对象?

使用TypeReference反序列化的例子

有以下类定义:

class Result {
	private long code;
	private T data;
}

class People {
	private String name;
}

实例化以下对象:

Result<List> result = new Result();
List peopleList = new ArrayList();
People xiaoHong = new People("小红");
People xiaoMing = new People("小明");
peopleList.add(xiaoHong);
peopleList.add(xiaoMing);
result.setData(peopleList);

其对应的JSON字符串为:

{"code":0,"data":[{"name":"小红"},{"name":"小明"}]}

下面使用jackson的TypeReference来反序列化字符串为以上的对象:

String jsonStr =  "{\"code\":0,\"data\":[{\"name\":\"小红\"},{\"name\":\"小明\"}]}";
TypeReference<Result<List>> typeReference = new TypeReference<Result<List>>() {};
ObjectMapper objectMapper = new ObjectMapper();
Result<List> listResult = objectMapper.readValue(jsonStr, typeReference);

当我们构造http客户端的时候,有时候不想让用户看到类似Result<List>这样的返回,只想给用户List这样的数据,那怎么动态构造TypeReference呢?

根据泛型动态构造TypeReference

我们还是以一个实际的例子来说明,假设我们提供了一个http客户端工具,它的定义是这样的。

class HttpClient {
	public  T get(String url, Map params, TypeReference typeR) {
		//获取jsonStr, 假设这里获取到的是:{"code":0,"data":[{"name":"小红"},{"name":"小明"}]}
		String jsonStr = execute(url, params);
		//todo 解析为枚举类型,下面会讲解如何实现
	}
}

假设我们这样调用HttpClient:

HttpClient client = new HttpClient();
//注意这里的TypeReference的泛型
List result = client.get("http://xxxx", params, new TypeReference<List>());

额额额???为啥不是TypeReference<Result<List>> ???

这就是我们要达到的目的,即用户不关注外层包裹的Result,只需要指定实际想要的类型即可。下面我们来看下如何实现。

带泛型的类如何表示?

首先我们实现一个ParameterizedType的子类,至于这个类的作用请往下看:

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class ParameterizedTypeImpl implements ParameterizedType, Serializable {
    private final Type[] actualTypeArguments;
    private final Type ownerType;
    private final Type rawType;

    public ParameterizedTypeImpl(Type[] actualTypeArguments, Type ownerType, Type rawType) {
        this.actualTypeArguments = actualTypeArguments;
        this.ownerType = ownerType;
        this.rawType = rawType;
    }

    @Override
    public Type[] getActualTypeArguments() {
        return this.actualTypeArguments;
    }

    @Override
    public Type getRawType() {
        return this.rawType;
    }

    @Override
    public Type getOwnerType() {
        return this.ownerType;
    }
}

我们来解释一下这个类,先看一下这个类的继承链:

ParameterizedTypeImpl implements ParameterizedType extends Type
  • Type接口的语义是指:类型,标识java里的所有类。(注意:Class类也是Type的实现类,下面会用到)
  • ParameterizedType接口的语义是:带泛型的类。

在java9之前jdk是包含了ParameterizedTypeImpl的实现的,但在jdk9及以后就没了,所以保险起见我们自己需要实现一下。

我们再看一下TypeReference的定义:

public abstract class TypeReference implements Comparable<TypeReference> {
    protected final Type _type;

    protected TypeReference() {
        Type superClass = this.getClass().getGenericSuperclass();
        if (superClass instanceof Class) {
            throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
        } else {
            this._type = ((ParameterizedType)superClass).getActualTypeArguments()[0];
        }
    }

    public Type getType() {
        return this._type;
    }

    public int compareTo(TypeReference o) {
        return 0;
    }
}

这里的_type成员变量即ParameterizedTypeImpl类型。

我们继续看ParameterizedTypeImpl几个成员变量的意思:(此处以:new TypeReference<Result<List>>为例)

  • rawType

    此处的rowType=Result.class,即原始的类是什么

  • actualTypeArguments

    此处的actualTypeArguments=new Type[]{List.class},为什么是数组类型,是因为泛型里面可以放多个类型,我可以这么放:Result<List,Set>,那么这里的actualTypeArguments=new Type[]{List.class,Set},因为是一个Type数组,而每个Type又是一个ParameterizedType,所以可以向下继续查看(嵌套)。

  • ownerType

    所谓的ownerType只在嵌套类中会出现,其他情况为空。举个例子:I.S,则S的ownerType=I

完成HttpClient的实现

class HttpClient {	public  T get(String url, Map params, TypeReference typeR) {		//获取jsonStr, 假设这里获取到的是:{"code":0,"data":[{"name":"小红"},{"name":"小明"}]}		String jsonStr = execute(url, params);		ObjectMapper objectMapper = new ObjectMapper();		//在此处偷梁换柱(所谓的动态构造其实就一句话)        TypeReference<Result> resultType = new TypeReference<Result>>() {            @Override            public Type getType() {            	//在外层再包一层Result,就是所谓的动态构造,是不是很简单!!!                return new ParameterizedTypeImpl(new Type[]{typeR.getType()},null, Result.class);            }        };        Result listResult = objectMapper.readValue(jsonStr, resultType);        //此处省略了一下异常判断,不可直接这么使用		return listResult.getData();	}}

然后我们就可以这么调用了:

HttpClient client = new HttpClient();
//注意这里的TypeReference的泛型
List result = client.get("http://xxxx", params, new TypeReference<List>());

OK,完结!

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://www.net2asp.com/01330f6136.html