Spring Boot RestTemplate将JSON响应转换为实体类失败问题排查
问题排查与修正方案
错误原因
代码存在5处核心问题,导致反序列化失败:
- 未匹配JSON最外层结构:接口返回的JSON最外层存在
main包装节点,当前直接以Main.class作为反序列化目标,框架会直接把最外层JSON对象和Main类字段匹配,找不到test、response字段直接报错。 response字段类型与JSON结构完全不符:JSON中response是数组结构,数组元素是包含responseTime、redirects数字字段的对象,当前代码把response定义为单个自定义Response类型,且Response类中凭空定义了不存在于JSON结构的responseMap字段,完全无法匹配。- 日期字段缺少反序列化配置:Test类中
Date字段是java.util.Date类型,JSON中对应值是yyyy-MM-dd格式的字符串,默认序列化框架无法直接将该格式字符串转为Date对象。 - 字段命名匹配风险:Java字段命名规范为驼峰式首字母小写,当前Test类中
Date、Id字段直接首字母大写,和框架默认的属性匹配规则存在冲突,容易出现字段找不到的问题。 - 实体类缺少必要方法:RestTemplate默认使用Jackson做JSON序列化,需要实体类提供无参构造、对应字段的getter/setter才能通过反射完成属性赋值,当前代码未提供这些方法。
修正方案
按以下步骤修改即可:
- 新增最外层响应包装类,匹配JSON最外层的
main节点 - 调整
response字段类型:按照JSON数组结构,用List<Map<String, Object>>类型接收,既符合数组结构,也满足将response内容映射为Map(框架默认会用HashMap实例化Map接口)的需求;注意Map的值类型要设为Object,因为responseTime、redirects都是数字类型,设为String会触发类型转换错误。 - 为Test类的日期字段指定反序列化格式,通过注解绑定JSON中大写开头的字段名,将Java字段调整为符合规范的驼峰命名。
- 为所有实体类添加无参构造、对应字段的getter与setter。
- 修改RestTemplate调用代码,将反序列化目标类替换为新增的最外层包装类。
修正后完整代码
- 最外层响应类
public class ApiResponse { private Main main; public ApiResponse() {} public Main getMain() { return main; } public void setMain(Main main) { this.main = main; } }
- Main实体类
import java.util.List; import java.util.Map; public class Main { private Test test; private List<Map<String, Object>> response; public Main() {} public Test getTest() { return test; } public void setTest(Test test) { this.test = test; } public List<Map<String, Object>> getResponse() { return response; } public void setResponse(List<Map<String, Object>> response) { this.response = response; } }
- Test实体类
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Date; public class Test { @JsonProperty("Date") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") private Date date; @JsonProperty("Id") private Integer id; public Test() {} public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } }
- 调整后的接口调用代码
HttpEntity<String> request = new HttpEntity<>(setHeaders()); ResponseEntity<ApiResponse> response = restTemplate.exchange(endpoint, HttpMethod.GET, request, ApiResponse.class); // 调用getBody()拿到ApiResponse实例后,通过getMain()即可获取对应Main对象
补充说明:如果必须保留自定义Response类包装Map结构,不要在Response类中额外定义
responseMap这类JSON中不存在的字段,可以直接让Response继承HashMap<String, Object>,再将Main类中的response字段类型改为List<Response>即可。
内容的提问来源于stack exchange,提问作者perplexedDev
相关产品推荐
相关产品推荐

