【java基础】根据泛型动态构造jackson的TypeReference(json反序列化为带泛型的类的对象)
创始人
2025-05-29 02:46:06
0

根据泛型动态构造jackson的TypeReference

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

引出问题

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

使用TypeReference反序列化的例子

有以下类定义:

class Result {private long code;private T data;
}class People {private String name;
}

实例化以下对象:

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

当我们构造http客户端的时候,有时候不想让用户看到类似Result>这样的返回,只想给用户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>());

额额额???为啥不是TypeReference>> ???
这就是我们要达到的目的,即用户不关注外层包裹的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;}@Overridepublic Type[] getActualTypeArguments() {return this.actualTypeArguments;}@Overridepublic Type getRawType() {return this.rawType;}@Overridepublic 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> {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>>为例)

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

OK,完结!

相关内容

热门资讯

“中国保险经纪第一人”、江泰保... 红星资本局12月19日消息,据北京商报,近日,有传闻称,国内首家保险经纪公司——江泰保险经纪股份有限...
精确减持华谊兄弟至4.9999... “阿里系”减持,对华谊兄弟股权格局走向有何影响?作者 | 姚悦编辑丨于婞来源 | 野马财经此前,华谊...
潮汕女首富胞弟携立景创新冲刺I... 高管清一色立讯精密“老将”。作者|刘俊群编辑|刘钦文2025年的港股IPO赛道,“立讯系”的家族版图...
调岗太难了,所以他调走了 文/妖小妖(识局微信公共账号zhijuzk)局长办公室。老大情真意切,“老王啊,组织培养你不容易,你...
国盾量子:推举董事应勇暂代履行... 国盾量子 视觉中国 资料图12月19日晚间,科大国盾量子技术股份有限公司(688027,国盾量子)...
白银涨疯了!无风险套利轻松赚3... 图片来源:摄图网全文共2058字,阅读全文约需5分钟白银涨疯了,今年以来价格已经翻倍。本文来自“财小...
业绩股价“双杀”,投资者喊话管... 本报(chinatimes.net.cn)记者胡梦然 深圳摄影报道对于投资者而言,当业绩承压、股价低...
又一资金盘爆雷,碰瓷华为、港交... 记者丨郭聪聪编辑丨周炎炎视频|见习编辑陈泽锴在我国监管部门持续强化虚拟货币风险整治的背景下,以“煜志...
又一家控制权变更计划生变!南都... 本文来源:时代周报 作者:雨辰筹划控制权变更一周后,南都电源(300068.SZ)控制权变更计划按下...
不止工具!成立9年,小鹅通要成... 本文来源:时代周报 作者:高浩翔成立9年,小鹅通仍在不断拓宽边界。 小鹅通创始人、CEO 鲍春健,...