如何使用Jackson反序列化嵌套JSON数组映射为User实体列表
前置注意点:你定义的User类需要补全name、age字段的getter/setter方法,Jackson默认无法访问私有字段完成赋值,嫌手写麻烦可以直接加Lombok的@Data注解省略重复代码。
针对两层嵌套JSON解析拿User数组的需求,有三个常用落地方案,按需选择即可:
方案1:定义嵌套结构映射类(最推荐,零额外配置,可维护性最高)
这个接口返回的JSON结构非常规整,外层是response节点,节点内部是存储User数组的result节点,直接写对应结构的映射类即可,完全适配Jackson默认解析规则,后续接口新增错误码、提示信息等字段时扩展也很方便。
先写API响应的映射类:
public class UserApiResponse { private ResponseDetail response; // getter/setter public ResponseDetail getResponse() { return response; } public void setResponse(ResponseDetail response) { this.response = response; } // 映射response节点下的内容 public static class ResponseDetail { private User[] result; // getter/setter public User[] getResult() { return result; } public void setResult(User[] result) { this.result = result; } } }
调用时先获取完整响应,再取出目标数组即可:
UserApiResponse resp = restTemplate.getForObject(url, UserApiResponse.class); User[] users = resp.getResponse().getResult();
如果需要List类型的返回,直接用Arrays.asList(users)转换即可,也可以直接把映射类里的result字段类型定义为List<User>。
方案2:用JsonNode树模型定位节点(不想额外写映射类时用)
如果不想为了这个接口单独新增响应映射类,可以直接把响应读成Jackson的树结构,手动定位到result节点后再转成User数组,不需要额外依赖:
// 先把响应解析为JsonNode根节点 JsonNode rootNode = restTemplate.getForObject(url, JsonNode.class); // 逐层定位到目标数组,用path方法不会因为节点缺失抛出空指针 JsonNode resultNode = rootNode.path("response").path("result"); // 复用RestTemplate内置的ObjectMapper做类型转换 ObjectMapper objectMapper = restTemplate.getMappingJackson2HttpMessageConverter().getObjectMapper(); User[] users = objectMapper.treeToValue(resultNode, User[].class);
这个方案代码量小,适合临时调用接口的场景,不需要维护额外的类。
方案3:自定义解析逻辑(完全匹配你预期的直接调用方式)
如果你就想直接写User[] users = restTemplate.getForObject(url, User[].class),不想在调用层做任何节点处理,可以给RestTemplate加自定义的Jackson解析逻辑,遇到要转User[]类型的请求时,自动定位到嵌套的result节点解析:
RestTemplate customRestTemplate = new RestTemplate(); // 自定义Jackson消息转换器,增加嵌套结构解析规则 MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(){ @Override public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException { // 仅当目标类型是User数组时走自定义嵌套解析 if (type == User[].class) { JsonNode root = getObjectMapper().readTree(inputMessage.getBody()); JsonNode result = root.path("response").path("result"); return getObjectMapper().treeToValue(result, type); } // 其他类型仍然走默认解析逻辑 return super.read(type, contextClass, inputMessage); } }; // 把自定义转换器放到转换器列表最前面,优先匹配 customRestTemplate.getMessageConverters().add(0, converter);
配置完成后就可以直接按你预期的方式调用:
User[] users = customRestTemplate.getForObject(url, User[].class);
注意:这个自定义RestTemplate只适合调用返回结构和当前一致的接口,如果要调用其他结构不同的接口,不要复用这个实例,避免解析错误。
内容的提问来源于stack exchange,提问作者pwasoutside

