[代码段] [fastjson2]解析 通用泛型框架工具类解析 List获取泛型

技术实战 技术实战 2536 人阅读 | 0 人回复

Json是开发过程中饶过不去的一种格式,而且这种格式非常的通用。

而FastJson2 是一种使用广泛的解析工具,对于一些工具、框架性质的类,又经常不可避免的需要使用到Java的泛型,本文就简单记录下泛型场景下,JSON的数据处理解析。

主要解决Fastjson解析泛型,嵌套类型等。

常用的交互模型

先介绍下一般常用的交互格式(请求响应类似的道理,以响应举例):

一种非常常见的格式化模板代码为:

public class ResponseBean<RS> {
    private Integer code;
    private String msg;
    private RS data;

}

对应的Json串的形式如下,其中data节点也可能没有,也可能嵌套多层:

{
  "code":1,
  "msg":"成功",
  "data": {
       ...........
  }
}

响应返回的分类

(1)最基础格式,没有嵌套,单纯地字段与对应的值,返回请求与响应,也可能包含更多的一些其他字段

{
  "code": 1,
  "msg": "成功"
}

(2)稍微复杂点,对象层次的嵌套,不涉及到List<>也就是没有数组。

从类设计的视角看,包含了一个对象,这个对象包含两个属性

{
  "code":1,
  "msg":"成功",
  "data": {
    "position": 1,
    "show": true
  }
}

(3)返回字段与List<>,并且通常使用data作为对象的key,也就是返回码、返回消息单独字段,数据体包含在data中,并且

{
  "code": 1,
  "msg": "识别成功",
  "data": [
    {
      "positionId": 1,
      "x1": 0,
      "y1": 0,
      "x2": 200,
      "y2": 200
    },
    {
      "positionId": 2,
      "x1": 5,
      "y1": 10,
      "x2": 36,
      "y2": 21
    },
    {
      "positionId": 3,
      "x1": 44,
      "y1": 55,
      "x2": 66,
      "y2": 77
    },
    {
      "positionId": 4,
      "x1": 24,
      "y1": 22,
      "x2": 58,
      "y2": 96
    }
  ]
}

按照通用的交互式逻辑开发的话,返回的响应基本上就是这样几种格式,无非是data 中样式的不断变化。

FastJson 对这些形式的解析,都是完全没有问题的,重点是与泛型纠缠在一起的时候,如何能够根据实际类型进行解析。

通用泛型解析

下面示例记录下一种通用的解决方式,以及子类父类泛型的解析处理。

RequestBeanResponseBean 是请求的实体,而RequestBodyResponseBody 则是内部的数据体

image.png

请求

package com.example.demo.beans;

/**
 * 请求实体
 * @author 程序员潇然 https://crazybytex.com/
 */
public class RequestBean <RQ>{
    //还可以保存一些通用请求参数

    private RQ data;

    public RQ getData() {
        return data;
    }

    public void setData(RQ data) {
        this.data = data;
    }
}

请求体

package com.example.demo.beans;

/** 请求体
 * @author 程序员潇然 https://crazybytex.com/
 */
public class RequestBody {
}

响应

package com.example.demo.beans;
/**
 * 响应实体
 * @author 程序员潇然 https://crazybytex.com/
 */
public class ResponseBean<RS> {
    private Integer code;
    private String msg;
    private RS data;

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public RS getData() {
        return data;
    }

    public void setData(RS data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "ResponseBean{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}

响应体

package com.example.demo.beans;

/**
 * 响应体
 *
 * @author 程序员潇然 https://crazybytex.com/
 */
public class ResponseBody {

}

通用处理器

package com.example.demo;

import com.alibaba.fastjson2.JSON;
import com.example.demo.beans.RequestBean;
import com.example.demo.beans.RequestBody;
import com.example.demo.beans.ResponseBean;
import com.example.demo.beans.ResponseBody;
import com.example.demo.sample.TestListType;
import com.example.demo.sample.TestResponse;
import com.example.demo.sample.TestqRequest;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

/**
 * 通用的执行器 样例,比如可以设计为模板模式
 * 抽象一些通用处理逻辑,子类负责具体的某些请求响应的实现  并且通过子类指定请求与响应的泛型
 * 比如通用的封装的发起http请求 通用的rpc调用处理等
 *
 * @param <RQ>
 * @param <RS>
 * @author 程序员潇然 https:crazybytex.com
 */
public class HttpExecutor<RQ, RS> {

    public ResponseBean<RS> doPost(RequestBean<RQ> requestBean) {

        //此处假设为执行某些逻辑处理、调用之后,获取到的响应结果,结果是json的
        String resultJson1 = "{\n" +
                "    \"code\": 45,\n" +
                "    \"msg\": \"laboris eu irure voluptate\"\n" +
                "}";

        String resultJson2 = "{\"code\":1,\"data\":{\"a\":1,\"b\":5,\"x\":\"feffewef\"},\"msg\":\"成功\"}";


        String resultJson3 = "{\n" +
                "  \"code\": 1,\n" +
                "  \"msg\": \"识别成功\",\n" +
                "  \"data\": [\n" +
                "    {\n" +
                "      \"classId\": 1,\n" +
                "      \"x1\": 0,\n" +
                "      \"y1\": 0,\n" +
                "      \"x2\": 200,\n" +
                "      \"y2\": 200\n" +
                "    },\n" +
                "    {\n" +
                "      \"classId\": 2,\n" +
                "      \"x1\": 5,\n" +
                "      \"y1\": 10,\n" +
                "      \"x2\": 36,\n" +
                "      \"y2\": 21\n" +
                "    },\n" +
                "    {\n" +
                "      \"classId\": 3,\n" +
                "      \"x1\": 44,\n" +
                "      \"y1\": 55,\n" +
                "      \"x2\": 66,\n" +
                "      \"y2\": 77\n" +
                "    },\n" +
                "    {\n" +
                "      \"classId\": 4,\n" +
                "      \"x1\": 24,\n" +
                "      \"y1\": 22,\n" +
                "      \"x2\": 58,\n" +
                "      \"y2\": 96\n" +
                "    }\n" +
                "  ]\n" +
                "}";

        String resultJson = resultJson3;
/******************************************************************************************************************/
        ResponseBean<RS> responseBean = (ResponseBean<RS>) JSON.parseObject(resultJson, ResponseBean.class);
        Map<String, Object> map = JSON.parseObject(resultJson, Map.class);
        if (map.get("data") != null) {
            //请求参数
            Type requestParameterizedType = ((ParameterizedType) getClass().getGenericSuperclass())
                    .getActualTypeArguments()[0];

            //响应参数
            Type responseParameterizedType = ((ParameterizedType) getClass().getGenericSuperclass())
                    .getActualTypeArguments()[1];
            responseBean.setData(JSON.parseObject(JSON.toJSONString(map.get("data")), responseParameterizedType));

        }

        return responseBean;
    }


    public static void main(String[] args) {
        /*
        注意,下面是简化形式的子类创建,匿名类,注意new 后面有一个{}
        这样就可以指定 请求以及响应类型
         */
//        RequestBean<RequestBody> requestBean1 = new RequestBean<>();
//        ResponseBean<ResponseBody> responseBean1 = new HttpExecutor<RequestBody, ResponseBody>() {
//        }.doPost(requestBean1);
//        System.out.println(responseBean1);


//        RequestBean<RequestBody> requestBean2 = new RequestBean<>();
//        ResponseBean<TestResponse> responseBean2 = new HttpExecutor<RequestBody, TestResponse>() {
//        }.doPost(requestBean2);
//        System.out.println(responseBean2);
//
        RequestBean<RequestBody> requestBean3 = new RequestBean<>();
        ResponseBean<List<TestListType>> responseBean3 = new HttpExecutor<RequestBody, List<TestListType>>() {
        }.doPost(requestBean3);
        System.out.println(responseBean3);


    }
}

通过:

调整String resultJson = resultJson3; 这行代码控制测试数据

main 方法中放开对应的序号请求

可以进行测试查看结果。

完整结构如下:

image.png

样例bean

package com.example.demo.sample;

import com.example.demo.beans.ResponseBody;

public class TestResponse extends ResponseBody {

    private Integer a;
    private Integer b;
    private String x;

    public Integer getA() {
        return a;
    }

    public void setA(Integer a) {
        this.a = a;
    }

    public Integer getB() {
        return b;
    }

    public void setB(Integer b) {
        this.b = b;
    }

    public String getX() {
        return x;
    }

    public void setX(String x) {
        this.x = x;
    }

    @Override
    public String toString() {
        return "TestResponse{" +
                "a=" + a +
                ", b=" + b +
                ", x='" + x + '\'' +
                '}';
    }
}
package com.crazybytex.hello.fastjson.sample;


public class TestListType {
    private Integer classId;
    private Integer x1;
    private Integer y1;
    private Integer x2;
    private Integer y2;
    public Integer getClassId() {
        return classId;
    }

    public void setClassId(Integer classId) {
        this.classId = classId;
    }

    public Integer getX1() {
        return x1;
    }

    public void setX1(Integer x1) {
        this.x1 = x1;
    }

    public Integer getY1() {
        return y1;
    }

    public void setY1(Integer y1) {
        this.y1 = y1;
    }

    public Integer getX2() {
        return x2;
    }

    public void setX2(Integer x2) {
        this.x2 = x2;
    }

    public Integer getY2() {
        return y2;
    }

    public void setY2(Integer y2) {
        this.y2 = y2;
    }

    @Override
    public String toString() {
        return "TestListType{" +
                "classId=" + classId +
                ", x1=" + x1 +
                ", y1=" + y1 +
                ", x2=" + x2 +
                ", y2=" + y2 +
                '}';
    }
}

代码位于gitee上的代码测试库中: https://gitee.com/crazybytex/java-code-fragment/tree/master/src/main/java/com/crazybytex/fragment/fastjson

FastJson 解析的类型

通常对于对象的解析大概就是这种形式

JSON.parseObject(resultJson, Map.class);

这种就是字符串,类型

static <T> T parseObject(String text, Class<T> clazz) {

除了Class,另外还有:

static <T> T parseObject(String text, Type type) {

这个Type

package java.lang.reflect;

/**
 * Type is the common superinterface for all types in the Java
 * programming language. These include raw types, parameterized types,
 * array types, type variables and primitive types.
 *
 * @since 1.5
 */
public interface Type {
    /**
     * Returns a string describing this type, including information
     * about any type parameters.
     *
     * @implSpec The default implementation calls {@code toString}.
     *
     * @return a string describing this type
     * @since 1.8
     */
    default String getTypeName() {
        return toString();
    }
}

使用泛型的话,经常是需要接触到这个类的,因为有了类型才能解析。 而Class其实也是他的子类型 image.png

所以想要了解清楚Json的解析,就需要对这块进行研究。

另外,还有一种解析形式,针对集合泛型的

static <T> T parseObject(String text, TypeReference typeReference, JSONReader.Feature ... features) {

使用形式如:

JSON.parseObject(JSON.toJSONString(map.get("data")), new TypeReference<List<Response>>() {
                    })

需要注意到的是这是一个抽象类,所以使用的是匿名类

public abstract class TypeReference<T>

common_log.png 转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-89-1-1.html

关注下面的标签,发现更多相似文章
    黄小斜学Java

    疯狂的字节X

  • 目前专注于分享Java领域干货,公众号同步更新。原创以及收集整理,把最好的留下。
    包括但不限于JVM、计算机科学、算法、数据库、分布式、Spring全家桶、微服务、高并发、Docker容器、ELK、大数据等相关知识,一起进步,一起成长。
热门推荐
[若依]微服务springcloud版新建增添加一个
[md]若依框架是一个比较出名的后台管理系统,有多个不同版本。
[CXX1300] CMake '3.18.1' was not
[md][CXX1300] CMake '3.18.1' was not found in SDK, PATH, or
海康摄像头接入 wvp-GB28181-pro平台测试验
[md]### 简介 开箱即用的28181协议视频平台 `https://github.c