使用Spring RestTemplate如何将结构不同的REST JSON响应转为自定义模型对象
方案合理性说明
你当前使用Jackson JsonNode 手动提取字段的方案是完全合理的,适配你不需要全量响应字段、需统一业务模型做跨接口数据校验的场景,灵活度很高。
当前方案优化点
你现有实现可以做几个小调整提升稳定性和性能:
- 不要每次调用都新建
ObjectMapper实例,Spring Boot已经自动配置了单例的ObjectMapper,直接注入复用即可,避免重复创建的性能开销 - 提取字段时优先用
path()代替get(),遇到不存在的字段不会抛出空指针,同时使用asText()、asInt()等方法代替toString(),避免取出的字符串自带多余引号 - 你的响应体是JSON对象不是数组,不需要调用
get(0)获取首元素,直接读取根节点字段即可
优化后的代码示例:
@Autowired private ObjectMapper objectMapper; @Override public NotaryDistrictDetails getNotaryDistrictDetails(String districtId) throws URISyntaxException { String completeURL = this.wpPortalGetNotaryDistrictPostDetailsURL.replace("{districtId}", districtId); URI uri = new URI(completeURL); NotaryDistrictDetails myModelObj = new NotaryDistrictDetails(); try { HttpHeaders basicAuthHeader = this.getWpBasicAuthenticationHeader(); HttpEntity<String> request = new HttpEntity<>(basicAuthHeader); ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, request, String.class); if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) { JsonNode rootNode = objectMapper.readTree(response.getBody()); // 提取根节点id myModelObj.setId(rootNode.path("id").asText()); // 提取嵌套的title字段 myModelObj.setTitle(rootNode.path("title").path("rendered").asText()); // 提取深层嵌套的公证人ID myModelObj.setNotaryId(rootNode.path("toolset-meta") .path("gruppo-scheda-notary") .path("idnotary") .path("raw").asText()); // 其余字段赋值逻辑 } } catch(Exception e) { e.printStackTrace(); } return myModelObj; }
更优实现方案
根据你的场景可以选择以下两种更优方案:
方案1:JsonPath简化嵌套字段提取
如果响应嵌套层级深、需要提取的字段较多,推荐使用JsonPath库直接通过路径表达式提取字段,无需逐层调用get()方法,代码更简洁。
使用示例:
- Spring Boot默认已集成JsonPath,无需额外引入依赖
- 代码实现:
DocumentContext context = JsonPath.parse(response.getBody()); // 直接通过路径表达式提取任意层级字段 String notaryId = context.read("$.toolset-meta.gruppo-scheda-notary.idnotary.raw", String.class); String title = context.read("$.title.rendered", String.class); Integer id = context.read("$.id", Integer.class); // 赋值给业务模型 myModelObj.setId(String.valueOf(id)); myModelObj.setTitle(title); myModelObj.setNotaryId(notaryId);
方案2:静态内部类做中间反序列化层(推荐固定映射场景)
如果字段映射逻辑固定,想要获得编译期类型校验能力,避免运行期字段名写错的问题,可以在你的业务模型类内部定义静态的临时反序列化类,不需要单独维护一套外部响应模型,兼顾类型安全和代码整洁。
示例代码:
@Data public class NotaryDistrictDetails { // 你的业务模型原有字段 private String id; private String title; private String notaryId; // 内部静态反序列化中间类,仅当前类使用,无需对外暴露 @JsonIgnoreProperties(ignoreUnknown = true) @Data private static class WpApiResponse { private Integer id; private WpRenderField title; @JsonProperty("toolset-meta") private WpToolsetMeta toolsetMeta; } @JsonIgnoreProperties(ignoreUnknown = true) @Data private static class WpRenderField { private String rendered; private String raw; } @JsonIgnoreProperties(ignoreUnknown = true) @Data private static class WpToolsetMeta { @JsonProperty("gruppo-scheda-notary") private WpNotaryGroup notaryGroup; } @JsonIgnoreProperties(ignoreUnknown = true) @Data private static class WpNotaryGroup { @JsonProperty("idnotary") private WpRenderField idNotary; } // 统一转换方法 public static NotaryDistrictDetails convertFrom(WpApiResponse response) { NotaryDistrictDetails details = new NotaryDistrictDetails(); details.setId(String.valueOf(response.getId())); details.setTitle(response.getTitle().getRendered()); details.setNotaryId(response.getToolsetMeta().getNotaryGroup().getIdNotary().getRaw()); return details; } }
调用时直接让RestTemplate反序列化为中间类即可:
ResponseEntity<NotaryDistrictDetails.WpApiResponse> response = restTemplate.exchange(uri, HttpMethod.GET, request, NotaryDistrictDetails.WpApiResponse.class); NotaryDistrictDetails details = NotaryDistrictDetails.convertFrom(response.getBody());
方案选型建议
- 字段少、映射逻辑不常变更:用优化后的JsonNode方案即可,足够轻量
- 嵌套层级深、字段多:用JsonPath方案,代码更简洁
- 映射逻辑固定、需要类型安全校验:用内部静态类中间层方案,性能最优、稳定性最高
内容的提问来源于stack exchange,提问作者AndreaNobili
相关产品推荐
相关产品推荐

