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

文章讲述了如何在Java中使用Jackson库处理泛型类型的反序列化,特别是如何动态构造TypeReference对象。通过示例展示了如何从JSON字符串反序列化到Result<List<People>>,并讨论了在HTTP客户端实现中如何让用户只需指定如List<People>这样的类型,而隐藏外层的Result包装。最后,文章提供了一种实现方式,通过创建ParameterizedTypeImpl子类来动态构造所需的TypeReference。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引出问题

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

使用TypeReference反序列化的例子

有以下类定义:

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

class People {
	private String name;
}

实例化以下对象:

Result<List<People>> result = new Result<>();
List<People> 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<People>>> typeReference = new TypeReference<Result<List<People>>>() {};
ObjectMapper objectMapper = new ObjectMapper();
Result<List<People>> listResult = objectMapper.readValue(jsonStr, typeReference);

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

根据泛型动态构造TypeReference

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

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

假设我们这样调用HttpClient:

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

额额额???为啥不是TypeReference<Result<List<People>>> ???
这就是我们要达到的目的,即用户不关注外层包裹的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<T> implements Comparable<TypeReference<T>> {
    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<T> o) {
        return 0;
    }
}

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

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

  • rawType
    此处的rowType=Result.class,即原始的类是什么
  • actualTypeArguments
    此处的actualTypeArguments=new Type[]{List<People>.class},为什么是数组类型,是因为泛型里面可以放多个类型,我可以这么放:Result<List<People>,Set<People>>,那么这里的actualTypeArguments=new Type[]{List<People>.class,Set<People.class>},因为是一个Type数组,而每个Type又是一个ParameterizedType,所以可以向下继续查看(嵌套)。
  • ownerType
    所谓的ownerType只在嵌套类中会出现,其他情况为空。举个例子:I<T>.S<E>,则S<E>ownerType=I<T>

完成HttpClient的实现

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

        Result<T> listResult = objectMapper.readValue(jsonStr, resultType);
        //此处省略了一下异常判断,不可直接这么使用
		return listResult.getData();
	}
}

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

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

OK,完结!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值