跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

JSON 响应结构解析与 Java DTO 映射完整教程

本文详解如何根据嵌套 json 数组(每项含 employee 对象)设计精准匹配的 java dto 结构,并使用 spring resttemplate 正确反序列化为类型安全的集合。涵盖 pojo 建模、注解适配、泛型响应处理及常见调用陷阱。 本文详解如何根据嵌套 js on 数组(每项含 employee 对象)设计精准匹配的 java dto 结构,并使用 spring resttemplate 正确反序列化为类型安全的集合。涵盖 pojo 建模、注解适配、泛型响应处理及常见调用陷阱。 你提供的 JSON 是一个 顶层为 JSON 数组 ,数组中每个元素均为一个键为 "employee" 的对象,其值才是真正的员工数据(含内嵌的 employee_company)。这种结构在 REST API 中很常见——它并非扁平的员工列表,而是“包装器数组”,每一项都需先解包 employee 字段才能获取业务实体。 因此,DTO 设计必须严格对应层级关系。以下是推荐的、生产可用的 Java 类结构(基于 Jackson,默认无需额外注解,但建议显式标注以增强可读性与兼容性):
// 根节点:对应 JSON 数组中的每个 { "employee": { ... } } public class Root { @JsonProperty("employee") private Employee employee; // 必须提供无参构造器(Jackson 反序列化必需) public Root() {} public Employee getEmployee() { return employee; } public void setEmployee(Employee employee) { this.employee = employee; } } // 员工主体类 public class Employee { private int id; private String name; @JsonProperty("employee_company") private EmployeeCompany employeeCompany; public Employee() {} // getter/setter 略(实际开发中请补全) public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public EmployeeCompany getEmployeeCompany() { return employeeCompany; } public void setEmployeeCompany(EmployeeCompany employeeCompany) { this.employeeCompany = employeeCompany; } } // 公司信息类 public class EmployeeCompany { private int id; private String name; public EmployeeCompany() {} public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
✅ 关键点说明: Root 类代表 JSON 数组的 每个元素 ,不是整个响应;它不叫 EmployeeDTO,因为它的语义是“带包装的员工数据”。 @JsonProperty("employee_company") 确保下划线命名字段正确映射(若 JSON 键名与 Java 驼峰不一致,此注解必不可少)。 所有类 必须有 public 无参构造器 ,否则 Jackson 会抛 InstantiationException。 正确调用 RestTemplate 获取列表 你不能直接用 restTemplate.getForObject(url, EmployeeDTO.class) —— 因为 EmployeeDTO.class 是单个对象类型,而响应是 List。正确方式如下: Find JSON Path Online Easily find JSON paths within JSON objects using our intuitive Json Path Finder 下载 立即学习 “ Java免费学习笔记(深入) ”; ✅ 推荐方案:使用 ParameterizedTypeReference
String url = "https://api.example.com/employees"; List rootList = restTemplate.exchange( RequestEntity.get(URI.create(url)) .headers(headers) .build(), new ParameterizedTypeReference>() {} ).getBody(); // 提取员工列表(去包装) List employees = rootList.stream() .map(Root::getEmployee) .filter(Objects::nonNull) .collect(Collectors.toList());
✅ 替代方案:封装响应包装类(更清晰的语义)
public class EmployeeResponse { private List content; // 或直接命名为 employees public List getContent() { return content; } public void setContent(List content) { this.content = content; } } // 调用: EmployeeResponse response = restTemplate.getForObject(url, EmployeeResponse.class); List employees = response.getContent().stream() .map(Root::getEmployee) .filter(Objects::nonNull) .collect(Collectors.toList());
⚠️ 注意事项与避坑指南 不要用 List 直接接收 :该 JSON 不是 [{"id":1,...}, ...],而是 [{"employee":{...}}, ...],强行映射会导致 JsonMappingException。 避免 Map 方案(如 List>) :丧失类型安全、无法享受 IDE 自动补全与编译检查,仅适合动态 Schema 场景,本例完全不适用。 字段命名一致性 :若后端可能切换为驼峰(如 "employeeCompany"),请统一使用 @JsonProperty 显式声明,而非依赖 PropertyNamingStrategies.SNAKE_CASE 全局配置(易引发隐式耦合)。 空值防护 :Root.getEmployee() 可能为 null(尽管示例中不存在),流式处理时务必 .filter(Objects::nonNull)。 依赖版本 :确保 jackson-databind ≥ 2.11(Spring Boot 2.3+ 默认满足),以支持现代泛型反序列化特性。 通过以上结构与调用方式,你将获得类型安全、可维护、符合 Spring 生态最佳实践的 JSON 消费代码。核心原则始终是: DTO 层级 = JSON 层级,宁可多一层包装类,不可牺牲结构准确性。

相关文章