如何使用Jackson/FasterXML读取XML时仅保留指定节点及子节点?
如何用Jackson FasterXML只读取XML中的指定节点?
针对你遇到的这个要从海量XML节点中只提取特定字段的需求,Jackson其实提供了很直接的解决方案——通过忽略未知字段+精准映射目标节点来实现,哪怕目标节点嵌套层级很深也没问题。下面一步步来解决你的问题:
1. 第一步:配置Jackson忽略未知字段
生产环境中employee标签下有大量你不需要的节点,首先得告诉Jackson不要因为这些未定义的字段抛出反序列化异常。有两种方式可以实现:
方式一:全局配置XmlMapper
XmlMapper objectMapper = new XmlMapper(); // 关闭"未知属性失败"的特性,忽略所有未在POJO中定义的字段 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
方式二:在POJO类上添加注解(更推荐,粒度更细)
直接在Employee类上加上@JsonIgnoreProperties(ignoreUnknown = true),这样只会忽略这个类下的未知字段,不会影响其他POJO的反序列化行为。
2. 第二步:设计对应的POJO结构
现在只需要映射first_name、last_name和嵌套的node100及其子节点。这里针对嵌套场景给出标准实现:
首先定义Node100的结构(对应它的子节点)
public class Node100 { // 因为<values>下是多个<value>节点,用List来接收,关闭自动包装 @JacksonXmlElementWrapper(localName = "values", useWrapping = false) private List<String> value; // getter和setter public List<String> getValue() { return value; } public void setValue(List<String> value) { this.value = value; } }
然后定义中间的父节点类(按实际嵌套层级调整)
假设node100嵌套在parent1 -> parent2层级下,对应定义两个父类:
// 对应嵌套的第二层父节点 public class Parent2 { @JacksonXmlProperty(localName = "node100") private Node100 node100; // getter和setter public Node100 getNode100() { return node100; } public void setNode100(Node100 node100) { this.node100 = node100; } } // 对应嵌套的第一层父节点 public class Parent1 { @JacksonXmlProperty(localName = "parent2") private Parent2 parent2; // getter和setter public Parent2 getParent2() { return parent2; } public void setParent2(Parent2 parent2) { this.parent2 = parent2; } }
最后调整Employee和Employees类
@JsonIgnoreProperties(ignoreUnknown = true) // 关键:忽略所有未定义的字段 public final class Employee { @JacksonXmlProperty(localName = "first_name") private String firstName; @JacksonXmlProperty(localName = "last_name") private String lastName; @JacksonXmlProperty(localName = "parent1") // 对应嵌套的第一层父节点 private Parent1 parent1; // getter和setter public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Parent1 getParent1() { return parent1; } public void setParent1(Parent1 parent1) { this.parent1 = parent1; } // 可以加个快捷方法,直接获取node100,不用逐层调用 public Node100 getNode100() { if (parent1 != null && parent1.getParent2() != null) { return parent1.getParent2().getNode100(); } return null; } } @JacksonXmlRootElement(localName = "employees") public final class Employees { // 用List代替数组,更灵活,避免空指针问题 @JacksonXmlElementWrapper(localName = "employee", useWrapping = false) private List<Employee> employees; // getter和setter public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } }
3. 第三步:调整反序列化代码
把之前的代码加上忽略未知字段的配置,然后测试:
public static void main(String[] args) throws IOException { XmlMapper objectMapper = new XmlMapper(); // 全局配置忽略未知属性(如果已经在POJO上加了注解,这一步可以省略) objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 读取XML文件 Path xmlPath = Paths.get("C:\\Users\\91895\\Downloads\\File\\XmlFile.xml"); String xmlContent = Files.readString(xmlPath, StandardCharsets.UTF_8); // 反序列化为POJO Employees employees = objectMapper.readValue(xmlContent, Employees.class); // 测试输出提取到的字段 for (Employee emp : employees.getEmployees()) { System.out.println("First Name: " + emp.getFirstName()); System.out.println("Last Name: " + emp.getLastName()); Node100 node100 = emp.getNode100(); if (node100 != null && node100.getValue() != null) { System.out.println("Node100 Values: " + String.join(", ", node100.getValue())); } System.out.println("---"); } }
几个关键注意点
- 确保
@JacksonXmlProperty的localName和XML中的节点名称完全一致(XML节点名称是大小写敏感的) - 如果
node100可能不存在,对应的字段要允许为null(不要用基本类型,用包装类或List这类引用类型) - 用
List代替数组,不仅更灵活,还能避免空指针问题
内容的提问来源于stack exchange,提问作者Pale Blue Dot
相关产品推荐
相关产品推荐

