json反序列化泛型对象;泛型是变化的,如何写出通用代码?(源码分析)(代码片段)

帅东 帅东     2023-03-29     189

关键词:

本文以fastjson为例,gson等其他序列化工具都类似。

json如何反序列化出带泛型的结果,这个网上应该很多教程,但本文想要实现更高难度的反序列化。比如:泛型参数在变化,怎么写出通用代码?看例2

先看使用

一共三个类,A/B/C,定义放文章最后了

例1:

如果想要序列化带泛型的对象

B<C> bc = JSON.parseObject(bStr, new TypeReference<B<C>>() 
        );

一行代码就搞定,但原理是什么呢?为什么不能直接用class?
因为java泛型是假的,java对泛型进行了类型擦除。
但是,利用反射却可以做到,反射是可以拿到泛型参数的。

例2:

在我们写框架代码的时候可能会遇见,第一层的对象我已经知道了,想要序列化第二层对象出来。比如:我们业务代码经常封装了responseBody
代码大概类似这样:


    "success":true,
    "msg":"错误信息",
    "data":

    

假设我们函数定义是直接返回data呢?(请求失败默认抛异常,不需要每个调用方都进行判断)
其实有两种方法

  1. TypeReference交给外部调用方进行new,那么每次都能精确的获取到泛型参数
  2. 那如果不想调用方那么麻烦,能不能直接用泛型做呢?

2如下:我们可以这样做么?

    private static <T> T getCbyB(String str) 
        B<T> b = JSON.parseObject(str, new TypeReference<B<T>>() 
        );
        return b.c;
    

答案是不行的。
原因是什么呢?

源码分析(TypeReference)

    protected TypeReference()
        Type superClass = getClass().getGenericSuperclass();

        Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];

        Type cachedType = classTypeCache.get(type);
        if (cachedType == null) 
            classTypeCache.putIfAbsent(type, type);
            cachedType = classTypeCache.get(type);
        

        this.type = cachedType;
    

这是TypeReference的无参构造函数,可以看到步骤:

  1. 获取父类
  2. 获取父类的泛型参数

有趣的是:获取到的类型竟然是T?泛型T并没有被翻译

原因很简单,刚刚说过了,java是类型擦除的,压根就不会在运行的时候给你解析出T的类型
在深入分析一下:TypeReference是利用了ParameterizedType这个类,里面的两个值 rawType、actualTypeArguments
rawType:当前类的类型
actualTypeArguments:当前类泛型的列表
那么我们就可以构造TypeReference了,但是你会发现TypeReference并没有那么容易扩展,特别是type定义都是final,根本无法修改

public class TypeReference<T> 
    static ConcurrentMap<Type, Type> classTypeCache
            = new ConcurrentHashMap<Type, Type>(16, 0.75f, 1);

    protected final Type type;

不过幸运的是,fastjson也提供了另外一个构造函数(GSON就没那么好运了,方法竟然不是protected的,不过也有解决办法)

protected TypeReference(Type... actualTypeArguments)
        Class<?> thisClass = this.getClass();
        Type superClass = thisClass.getGenericSuperclass();

        ParameterizedType argType = (ParameterizedType) ((ParameterizedType) superClass).getActualTypeArguments()[0];
        Type rawType = argType.getRawType();
        Type[] argTypes = argType.getActualTypeArguments();

        int actualIndex = 0;
        for (int i = 0; i < argTypes.length; ++i) 
            if (argTypes[i] instanceof TypeVariable &&
                    actualIndex < actualTypeArguments.length) 
                argTypes[i] = actualTypeArguments[actualIndex++];
            
            // fix for openjdk and android env
            if (argTypes[i] instanceof GenericArrayType) 
                argTypes[i] = TypeUtils.checkPrimitiveArray(
                        (GenericArrayType) argTypes[i]);
            

            // 如果有多层泛型且该泛型已经注明实现的情况下,判断该泛型下一层是否还有泛型
            if(argTypes[i] instanceof ParameterizedType) 
                argTypes[i] = handlerParameterizedType((ParameterizedType) argTypes[i], actualTypeArguments, actualIndex);
            
        

        Type key = new ParameterizedTypeImpl(argTypes, thisClass, rawType);
        Type cachedType = classTypeCache.get(key);
        if (cachedType == null) 
            classTypeCache.putIfAbsent(key, key);
            cachedType = classTypeCache.get(key);
        

        type = cachedType;
    

大致的意思就是,type可以被替换,替换代码如下:

    private static <T> T getCbyB(String str, Class<T> tClass) 
        // TypeReference<A<T>>这里T会被tClass代替,没有什么作用。删除T,也能编译通过,因为java类型擦除,最后才进行类型强转
        // A<T> at = JSON.parseObject(str, new TypeReference<A>(tClass) );

        // 但是T不能删除,因为TypeReference这个类里面强制要求,如果传Type类型,泛型参数要有两层
        // 因为这个方法就是为了让你替换第二层的type,如果没有第二层根本不需要用这个方法
        B<T> b = JSON.parseObject(str, new TypeReference<B<T>>(tClass) 
        );
        return b.c;
    

一行代码就搞定了
调用方:

		B<C> b = new B<>();
        b.age = 10;
        b.c = new C();
        b.c.name = "dong";
		// 序列化 -> b
        String bStr = JSON.toJSONString(b);
        System.out.println(bStr);
        // 反序列化 -> c
		C c = getCbyB(bStr, C.class);

是不是很有意思,这样自己就能指定到底要序列化哪个对象了。
如果是这样的泛型呢?

A<B<C>> abc = JSON.parseObject(aStr, new TypeReference<A<B<C>>>() 
        );

我还是不要A,只要B<C>
同理:

		// 错误做法:a.b这里会被解析成JSONObject,因为new TypeReference<A<T>>(tClass) 只能替换一层
        A<T> a = JSON.parseObject(str, new TypeReference<A<T>>(tClass) 
        );
        // 正确做法
        ParameterizedTypeImpl parameterizedType = new ParameterizedTypeImpl(new Class[]cClass, null, bClass);
        A<T> a = JSON.parseObject(str, new TypeReference<A<T>>(parameterizedType) 
        );

两层、三层都搞定了,N层也不是问题了

其他类

static class A<T> 
        public T b;

        @Override
        public String toString() 
            return "A" +
                    "b=" + b +
                    '';
        
    

    static class B<T> 
        public int age;
        public T c;

        @Override
        public String toString() 
            return "B" +
                    "age=" + age +
                    ", c=" + c +
                    '';
        
    

    static class C 
        public String name;

        @Override
        public String toString() 
            return "C" +
                    "name='" + name + '\\'' +
                    '';
        
    

如何将字符串反序列化为对象,然后在颤振中设置为泛型

】如何将字符串反序列化为对象,然后在颤振中设置为泛型【英文标题】:Howtodeserializeastringtoaobjectandthensetasagenericinflutter【发布时间】:2020-12-2601:21:42【问题描述】:所以我有这个api调用,它的响应形式为:boolean:successful、int:r... 查看详情

如何使用 Moshi 反序列化泛型类型?

】如何使用Moshi反序列化泛型类型?【英文标题】:HowToDeserializeGenericTypeswithMoshi?【发布时间】:2017-04-1810:17:12【问题描述】:假设我们有这个JSON:["__typename":"Car","id":"123","name":"ToyotaPrius","numDoors":4,"__typename":"Boat","id":"4567","name":"U... 查看详情

使用 GSON 反序列化的父对象的参数实例化子对象并使用泛型?

】使用GSON反序列化的父对象的参数实例化子对象并使用泛型?【英文标题】:InstantiatechildobjectwithparamsfromparentobjectbeingdeserializedwithGSONandusegenerics?【发布时间】:2016-02-0915:06:01【问题描述】:我大致有以下结构classMyDeserialParent<... 查看详情

怎样将json数据转换成匿名数组或者泛型对象(c#)

...类库来解析,比如Json.Net或建立相应实体后用内置的Json反序列化功能来反序列化。 查看详情

json泛型对象的序列化parseobjectbytypereference

publicstaticTparseObject(Stringtext,TypeReferencetype,Feature...features)  returnparseObject(text,type.type,ParserConfig.global,DEFAULT_PARSER_FEATURE,features);用法示例:TypeReference>>MAP_TYPE_RE 查看详情

使用 GSON 反序列化泛型类型

】使用GSON反序列化泛型类型【英文标题】:DeserializingGenericTypeswithGSON【发布时间】:2013-08-2604:20:52【问题描述】:在我的Android应用程序(使用Gson库)中实现Json反序列化时遇到一些问题我做过这样的课publicclassMyJson<T>publicLis... 查看详情

泛型/JSON JavaScriptSerializer C#

...ms应用程序。我正在尝试使用System.Web.Script.Serialization库反序列化对象。错误是:类型\'jsonWinForm.Cate 查看详情

泛型/JSON JavaScriptSerializer C#

...ms应用程序。我正在尝试使用System.Web.Script.Serialization库反序列化对象。错误是:类型\'jsonWinForm.Cate 查看详情

android:gson通过借助typetoken获取泛型参数的类型的方法

...进行Json和Java对象之间的转化,对于包含泛型的类的序列化和反序列化Gson也提供了很好的支持,感觉有点意思,就花时间研究了一下。由于Java泛型的实现机制,使用了泛型的代码在运行期间相关的泛型参数的类型... 查看详情

Jackson - 使用泛型类反序列化

】Jackson-使用泛型类反序列化【英文标题】:Jackson-Deserializeusinggenericclass【发布时间】:2012-07-2418:42:47【问题描述】:我有一个json字符串,我应该将其反序列化为以下类classData<T>intfound;Class<T>hits我该怎么做?这是通常的... 查看详情

java泛型是什么?一文带你吃透泛型(代码片段)

文章目录1.Java泛型2.泛型类3.泛型接口4.泛型方法5.泛型集合Java编程基础教程系列1.Java泛型Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递。其主要的形式有泛型类,泛型接口和泛型方法。泛型概... 查看详情

javase语法基础---泛型(基础知识问答)

文章目录如果没有泛型?什么是泛型,介绍一下泛型?各个版本泛型的使用及变化说一下?泛型的意义?泛型的好处?1.Java中的泛型是什么?使用泛型的好处是什么?2.Java的泛型是如何工作的或者什么是类型... 查看详情

基于反射和泛型的编程

  基于反射和泛型的编程,泛型是容器话的思路统过泛型对象或泛型方法,管理实际要操作的对象,以实现对于实际对象管理的一种编程。  比如导出excel生成报表这种在平台开发中及其普遍的需求,有多个对象有生成报表... 查看详情

GWT 反序列化 RPC 调用中的泛型列表

】GWT反序列化RPC调用中的泛型列表【英文标题】:GWTDeserializeGenericsListinRPCCall【发布时间】:2011-05-3013:28:51【问题描述】:我有一个返回可序列化对象列表的方法,我对该方法进行了RPC调用,一切顺利,直到反序列化过程:我收... 查看详情

搞懂fastjson对泛型的反序列化原理(代码片段)

fastjson是现在非常常用的一个json的序列化和反序列化工具,是阿里发布的,虽然最近一直在暴雷,各种漏洞,但是这不影响我们学习他。fastjson的使用加入依赖在pom.xml中直接增加fastjson的依赖就行了,向下面这... 查看详情

搞懂fastjson对泛型的反序列化原理(代码片段)

fastjson是现在非常常用的一个json的序列化和反序列化工具,是阿里发布的,虽然最近一直在暴雷,各种漏洞,但是这不影响我们学习他。fastjson的使用加入依赖在pom.xml中直接增加fastjson的依赖就行了,向下面这... 查看详情

querywrapper泛型是啥

参考技术A实体类。querywrapper是数据库应用中的一个类型数据段。querywrapper中的泛型是指一个实体类,代表字段中的where条件,以封装多数查询条件。泛型的本质是参数化类型,就是说所操作的数据类型被指定为一个参数。 查看详情

gson通过借助typetoken类来解决这个问题

...Gson包进行Json和Java对象之间的转化,对于包含泛型的类的序列化和反序列化Gson也提供了很好的支持,感觉有点意思,就花时间研究了一下。由于Java泛型的实现机制,使用了泛型的代码在运行期间相关的泛型参数的类型会被擦除... 查看详情