解决RestTemplate反序列化嵌套对象的问题

 更新时间:2022年11月17日 15:03:31   作者:小楼夜听雨QAQ  
这篇文章主要介绍了解决RestTemplate反序列化嵌套对象的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

RestTemplate反序列化嵌套对象

假设某个接口返回的数据如下格式

{
  "msg" : "ok",
  "code" : 0,
  "data" : {
    "id" : 1,
    "tasks" : [ {
      "id" : 300,
      "nodeId" : 801,
      "status" : 3,
      "actionName" : "pick",
      "wcsProcessName" : "rgv"
    }, {
      "id" : 301,
      "nodeId" : 720,
      "status" : 3,
      "actionName" : "move",
      "wcsProcessName" : "rgv"
    }, {
      "id" : 302,
      "nodeId" : 720,
      "status" : 3,
      "actionName" : "checker",
      "wcsProcessName" : "checker"
    }, {
      "id" : 303,
      "nodeId" : 801,
      "status" : 3,
      "actionName" : "checker",
      "wcsProcessName" : "checker"
    } ],
    "status" : 3
  }
}

仿写一个测试接口,用于返回这种格式的数据

    @PostMapping("/aiot/task/info")
    public R queryTask(@RequestBody Map map) {
        Integer taskId = (Integer) map.get("taskId");
        Map res = new HashMap();
        res.put("id", taskId);
        res.put("tasks", Arrays.asList(
                new TaskProcess(300, 801, 3, "pick", "rgv"),
                new TaskProcess(301, 720, 3, "move", "rgv"),
                new TaskProcess(302, 720, 3, "checker", "checker"),
                new TaskProcess(303, 801, 3, "checker", "checker")
                ));
 
        res.put("status", 3);
        return R.ok(res);
    }

客户端的代码如下

    @Test
    public void test() {
 
        HashMap<String, Integer> map = new HashMap<>();
        map.put("taskId", 1);
        ResponseEntity<WcsR> wcsRResponseEntity = restTemplate.postForEntity("http://localhost:8081/aiot/task/info", map, WcsR.class);
        WcsR wcsR = wcsRResponseEntity.getBody();
    }

方案一

一般情况下,我们会创建一个与服务端一致的通用值返回对象。

@Data
public class WcsR {
    private String msg;
 
    private Integer code;
 
    private Object data;
}

data的类型无法确定。

在客户端不知道类型的情况下,我们看下data会被解析成什么

    @Test
    public void test() {
 
        HashMap<String, Integer> map = new HashMap<>();
        map.put("taskId", 1);
        ResponseEntity<WcsR> wcsRResponseEntity = restTemplate.postForEntity("http://localhost:8081/aiot/task/info", map, WcsR.class);
        WcsR wcsR = wcsRResponseEntity.getBody();
        System.out.println(wcsR.getData().getClass().getName());
    }

运行结果

看后台的代码我们可以知道,data是一个对象,里面有三个变量,id(整型),tasks(对象数组),status(整型)。

下面尝试获取这些成员变量

    @Test
    public void test() {
 
        HashMap<String, Integer> map = new HashMap<>();
        map.put("taskId", 1);
        ResponseEntity<WcsR> wcsRResponseEntity = restTemplate.postForEntity("http://localhost:8081/aiot/task/info", map, WcsR.class);
        WcsR wcsR = wcsRResponseEntity.getBody();
        System.out.println(wcsR.getData().getClass().getName());
        LinkedHashMap dataMap = (LinkedHashMap) wcsR.getData();
        System.out.println("id:   " + dataMap.get("id").getClass().getName());
        System.out.println("tasks: " + dataMap.get("tasks").getClass().getName());
        System.out.println("status: " + dataMap.get("status").getClass().getName());
    }

结果

可以看到,数组被解析成了Arraylist, 基本类型被解析成对应的包装类型。

数组里面还是个对象,还会继续帮我们解析吗?测试代码如下

    @Test
    public void test() {
 
        HashMap<String, Integer> map = new HashMap<>();
        map.put("taskId", 1);
        ResponseEntity<WcsR> wcsRResponseEntity = restTemplate.postForEntity("http://localhost:8081/aiot/task/info", map, WcsR.class);
        WcsR wcsR = wcsRResponseEntity.getBody();
        LinkedHashMap dataMap = (LinkedHashMap) wcsR.getData();
//        System.out.println("id:   " + dataMap.get("id").getClass().getName());
//        System.out.println("tasks: " + dataMap.get("tasks").getClass().getName());
//        System.out.println("status: " + dataMap.get("status").getClass().getName());
        ArrayList tasks = (ArrayList) dataMap.get("tasks");
        System.out.println(tasks.get(0).getClass().getName());
    }

结果

即使再次嵌套,还是对象还是被解析成了LinkedHashMap。

可以得到结论,在没有提供对象类型的情况下,RestTemplate默认情况下是这么帮我们解析的:

所有的对象都解析LinkedHashMap, 数组解析为ArrayList,基本类型解析为Integer(以及其他的包装类)。

方案二

为什么说是在“没有提供对象类型的情况”?

这个例子中,最外层WcsR是我们自己提供的对象,假设我们提供所有的嵌套对象,

则可以定义以下对象用于接收返回值

最外层对象

@Data
public class WcsR {
    private String msg;
 
    private Integer code;
 
//    private Object data;
 
    private TaskDetail tasks;
}

第二层对象

@Data
public class TaskDetail {
    private Integer id;
 
    private List<TaskProcess>tasks;
 
    private Integer status;
}

第三层对象

@Data
public class TaskProcess {
    private Integer id;
 
    private Integer nodeId;
 
    private Integer status;
 
    private String actionName;
 
    private String wcsProcessName;
}

测试类

    @Test
    public void test() {
 
        HashMap<String, Integer> map = new HashMap<>();
        map.put("taskId", 1);
        ResponseEntity<WcsR> wcsRResponseEntity = restTemplate.postForEntity("http://localhost:8081/aiot/task/info", map, WcsR.class);
        WcsR wcsR = wcsRResponseEntity.getBody();
        System.out.println(wcsR.getData());
        System.out.println(wcsR.getData().getId());
        System.out.println(wcsR.getData().getTasks());
        System.out.println(wcsR.getData().getStatus());
        System.out.println(wcsR.getData().getTasks().get(0));
    }

测试结果

这种方法也是可以的。

总结

可以看出,反序列化的方案与SpringMvc的HttpMessageConvert有点类似,如果你提供了对象,则会按照对象的结构反序列化。如果没有提供变量,则会转化成map、List等结构。

如果接口比较少、字段比较多,可以用第二种方案。

如果接口比较多,字段比较少,并且字段数量都比较少时,为了避免定义过多的无用类,可以用第一种方案。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • maven打包失败踩坑的解决方式详解

    maven打包失败踩坑的解决方式详解

    最近因为新项目有很多自定义的jar包,而且占内存很多,就直接拷贝过来,新开了一个maven仓库,用的时候没有问题,但是打包一直编译不通过,下面这篇文章主要给大家介绍了关于maven打包失败踩坑的解决方式,需要的朋友可以参考下
    2023-05-05
  • 不使用myeclipse注册机得到myeclipse注册码的方法(myeclipse序列号)

    不使用myeclipse注册机得到myeclipse注册码的方法(myeclipse序列号)

    本文为大家介绍不使用myeclipse注册机就能得到myeclipse注册码(序列号)的方法, 运行下面的JAVA代码就可以了
    2014-01-01
  • Springboot分页插件使用实例解析

    Springboot分页插件使用实例解析

    这篇文章主要介绍了Springboot分页插件使用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • IDEA编译报错:Error:(2048,1024) java: 找不到符号的解决方案

    IDEA编译报错:Error:(2048,1024) java: 找不到符号的解决方案

    在使用 Lombok 的过程中,你是否曾遇到过 IDEA 编译报错 Error:(2048,1024) java: 找不到符号?下面就让我们来深入剖析这一问题的根源,并给出相应的解决方案,需要的朋友可以参考下
    2025-02-02
  • Java 利用binarySearch实现抽奖计算逻辑

    Java 利用binarySearch实现抽奖计算逻辑

    这篇文章主要介绍了Java 利用binarySearch实现抽奖计算逻辑,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-12-12
  • SpringBoot实现RabbitMQ三种使用方式

    SpringBoot实现RabbitMQ三种使用方式

    本文主要介绍了SpringBoot实现RabbitMQ三种使用方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • MyBatis中resultType属性的使用

    MyBatis中resultType属性的使用

    这篇文章主要介绍了MyBatis中resultType属性的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-09-09
  • MyBatis 引入映射器的方法

    MyBatis 引入映射器的方法

    本文通过实例代码给大家分享mybatis 引入映射器的方法,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2017-09-09
  • springboot中报错Invalid character found in the request的解决

    springboot中报错Invalid character found in 

    这篇文章主要介绍了springboot中报错Invalid character found in the request的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • 详解Java 中的 AutoCloseable 接口

    详解Java 中的 AutoCloseable 接口

    本文对 try-with-resources 语法进行了较为深入的剖析,验证了其为一种语法糖,同时给出了其实际的实现方式的反编译结果,相信你在看完本文后,关于 AutoCloseable 的使用你会有新的收获。
    2020-11-11

最新评论