Spring Boot中RestTemplate无法将嵌套JSON转换为POJO报错
问题根因
报错核心原因是第三方接口返回的JSON顶层为对象结构(以{起始,对应错误提示中的JsonToken.START_OBJECT),但代码中直接指定反序列化目标类型为List<BitPayRates>——该类型对应JSON数组结构(以[起始),Jackson反序列化时结构匹配失败直接抛出异常。
结合你提到的接口返回多层嵌套JSON的前提,你需要的新闻列表数据并不在JSON最外层,而是存储在顶层对象的某个子属性中,直接将整个响应转换为List必然失败。
解决方案
按以下步骤调整即可:
- 第一步:先定位目标数据的JSON层级
单独调用一次第三方接口获取完整响应样例,找到存储新闻数组的具体层级路径,典型的接口返回结构参考如下:
上述样例中目标新闻列表存储在{ "code": 200, "result": { "count": 87, "items": [ {"news_id": 1, "news_provider_name": "CoinDesk", "HEADLINE": "xxx", "news_link": "xxx", "related_image": "xxx"} ] } }result.items路径下,并非顶层节点。 - 第二步:编写JSON响应对应的层级包装类
不要直接用数据库实体类接收整个接口响应,按照定位到的JSON结构编写对应层级的包装类,所有包装类都添加@JsonIgnoreProperties(ignoreUnknown = true)注解自动忽略不需要的字段:
如果JSON嵌套层级更深,按照相同逻辑逐层定义节点类即可,不需要的字段直接不定义,注解会自动跳过解析。// 顶层响应对象 @JsonIgnoreProperties(ignoreUnknown = true) public class ThirdPartyNewsResponse { // 按实际JSON的顶层字段定义,比如样例中的code、result private Integer code; private ResultNode result; // 补全对应getter、setter } // 第二层result节点 @JsonIgnoreProperties(ignoreUnknown = true) public class ResultNode { private Integer count; // 如果JSON字段名和Java属性名不一致,用@JsonProperty指定映射关系 @JsonProperty("items") private List<BitPayRates> newsItems; // 补全对应getter、setter } - 第三步:调整RestTemplate的反序列化与入库逻辑
修改exchange方法的响应类型为自定义的顶层包装类,拿到响应后逐层取出目标新闻列表,判空后再调用JPA方法批量入库:// 原错误写法:响应类型指定为ParameterizedTypeReference<List<BitPayRates>> ResponseEntity<ThirdPartyNewsResponse> response = restTemplate.exchange( apiUrl, HttpMethod.GET, buildAuthHeaderEntity(), // 之前定义的带X-RapidAPI-Key请求头的HttpEntity ThirdPartyNewsResponse.class ); // 逐层取出新闻列表 ThirdPartyNewsResponse body = response.getBody(); if (body != null && body.getResult() != null) { List<BitPayRates> newsList = body.getResult().getNewsItems(); if (newsList != null && !newsList.isEmpty()) { bitPayRatesRepository.saveAll(newsList); } }
快速调试方案
如果暂时不想编写多层包装类,可以先用Jackson的树模型JsonNode接收响应,手动定位到目标数组节点后再转换为实体列表,适合快速验证逻辑:
ResponseEntity<JsonNode> response = restTemplate.exchange( apiUrl, HttpMethod.GET, buildAuthHeaderEntity(), JsonNode.class ); // 按实际层级路径定位数组节点,比如样例中的result -> items JsonNode newsArrayNode = response.getBody().path("result").path("items"); // 转换为实体列表 ObjectMapper objectMapper = new ObjectMapper(); List<BitPayRates> newsList = objectMapper.convertValue(newsArrayNode, new ParameterizedTypeReference<List<BitPayRates>>() {}); // 后续入库逻辑不变
补充注意:如果接口返回的字段名和
BitPayRates实体类的属性名不一致(比如接口返回headline全小写,实体写的是HEADLINE),需要在对应实体字段上添加@JsonProperty("接口实际字段名")做映射,否则会出现字段取值为空的问题。
内容的提问来源于stack exchange,提问作者Alex_Pap
相关产品推荐
相关产品推荐

