解决RestTemplate反序列化嵌套对象的问题
小楼夜听雨QAQ 人气:0RestTemplate反序列化嵌套对象
假设某个接口返回的数据如下格式
{ "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等结构。
如果接口比较少、字段比较多,可以用第二种方案。
如果接口比较多,字段比较少,并且字段数量都比较少时,为了避免定义过多的无用类,可以用第一种方案。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
加载全部内容