概述

在互联网数据交换中,XML 是一种流行格式。处理 XML 数据时,通常需要将其转换为更便于进一步处理的格式。本教程将探讨将 XML 转换为 Java 的哈希映射(HashMap)的不同方法,这是一种支持高效数据检索和操作的数据结构。

1. 设置

我们将使用以下库解析 XML 到 HashMap:

<employees>
    <employee>
        <id>654</id>
        <firstName>John</firstName>
        <lastName>Doe</lastName>
    </employee>
    <employee>
        <id>776</id>
        <firstName>Steve</firstName>
        <lastName>Smith</lastName>
    </employee>
</employees>

我们使用以下 POJO 来存储 XML 数据:

public class Employee {
    private String id;
    private String firstName;
    private String lastName;

    // standard getters and setters
}

设置一个通用的测试方法来验证所有情况的结果:

void verify(Map<String, Employee> employeeMap) {
    Employee employee1 = employeeMap.get("654");
    Employee employee2 = employeeMap.get("776");
    Assertions.assertEquals("John", employee1.getFirstName());
    Assertions.assertEquals("Doe", employee1.getLastName());
    Assertions.assertEquals("Steve", employee2.getFirstName());
    Assertions.assertEquals("Smith", employee2.getLastName());
}

2. 使用 XStream 解析 XML

XStream 是一个第三方库,用于对象与 XML 之间的序列化和反序列化。通过最小配置,XStream 可以提供解析 XML 数据的能力。

请添加以下 Maven 依赖:

<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.18</version>
</dependency>

创建一个 XStream 实例并设置别名:

XStream xStream=new XStream(); 
xStream.alias("employees", List.class); 
xStream.alias("employee", Employee.class);

为 XML 中的 employees 元素设置别名,表示为 Listemployee 元素设置别名表示为 Employee 对象。还需允许任何类型进行反序列化:

xStream.addPermission(AnyTypePermission.ANY);

使用 XStream 的 fromXML() 方法将 XML 字符串解析为 Employee 对象的列表:

List<Employee> employees = (List<Employee>) xStream.fromXML(xml);

然后将员工列表转换为 HashMap,使用 id 作为键,employee 对象本身作为值,利用 Java 流:

employees.stream().collect(Collectors.toMap(Employee::getId, Function.identity()))

3. 使用 Underscore-java 解析 XML

Underscore-java 是一个功能强大的工具库,提供了广泛的函数式编程和数据操作功能。它要求 Java 11 或更高版本。

添加以下 Maven 依赖:

<dependency>
    <groupId>com.github.javadev</groupId>
    <artifactId>underscore</artifactId>
    <version>1.89</version>
</dependency>

使用 Underscore-java 的 fromXmlMap() 函数解析 XML 字符串,并将其转换为嵌套的映射结构:

Map<String, Object> employeeList = (Map<String, Object>)U.fromXmlMap(xml).get("employees"); 
List<LinkedHashMap<String, String>> list=(List<LinkedHashMap<String,String>>)employeeList.get("employee"); 
parseXmlToMap(employeeMap, list);

提取 employees 元素,然后将 LinkedHashMap 转换为 HashMap

void parseXmlToMap(Map<String, Employee> employeeMap, List<LinkedHashMap<String, String>> list) {
    list.forEach(empMap -> {
        Employee employee = new Employee();
        for (Map.Entry<String, String> key : empMap.entrySet()) {
            switch (key.getKey()) {
            case "id":
                employee.setId(key.getValue());
                break;
            case "firstName":
                employee.setFirstName(key.getValue());
                break;
            case "lastName":
                employee.setLastName(key.getValue());
                break;
            default:
                break;
            }
        }
        employeeMap.put(employee.getId(), employee);
    });
}

遍历嵌套映射结构,根据数据创建 Employee 对象并填充字段。

4. 使用 Jackson 解析 XML

Jackson 是一个库,能通过注解或自定义配置无缝地将 XML 元素和属性映射到 Java 对象。

添加以下 Maven 依赖:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

使用 XmlMapper 特化映射器读取和写入 XML 数据:

XmlMapper xmlMapper = new XmlMapper();
Map<String, Object> map= xmlMapper.readValue(xml, Map.class);

读取 XML 数据并将其转换为键值对的映射。Jackson 动态解析 XML 并构建相应的映射结构。提取员工元素列表:

List<LinkedHashMap<String, String>> list= (List<LinkedHashMap<String, String>>) map.get("employee");

可以使用前面定义的 parseXmlToMap() 方法提取员工映射。

5. 使用 JAXB 解析 XML

JAXB 是 Java 架构对 XML 绑定的框架,支持使用注解将 XML 元素和属性绑定到 Java。

添加以下 Maven 依赖:

<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>2.3.3</version>
</dependency>

设置 Employees 类的注解,帮助将其绑定到 Java 对象:

@XmlRootElement(name = "employees")
public class Employees {

    private List<Employee> employeeList;

    @XmlElement(name = "employee")
    public List<Employee> getEmployeeList() {
        return employeeList;
    }
    // standard setter
}

创建一个 JAXBContext,用于管理 XML 数据和 Java 对象之间的绑定:

JAXBContext context = JAXBContext.newInstance(Employees.class); 
Unmarshaller unmarshaller = context.createUnmarshaller(); 
Employees employees = (Employees) unmarshaller.unmarshal(new StringReader(xmlData));

Unmarshaller 负责根据类上的 JAXB 注解将 XML 数据转换为对象。

将员工列表转换为 HashMap,使用 Java 流,与之前的方法相同。

6. 使用 DOM 解析器和 XPath 解析 XML

DOM 解析器 是一种无需第三方库解析 XML 的方式。它支持 XPath 用于遍历 XML 并提取数据。

创建一个 DOM 解析器工厂,用于解析 XML 文档:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
DocumentBuilder builder = factory.newDocumentBuilder(); 
Document doc = builder.parse(new InputSource(new StringReader(xmlData)));

使用负责构建 XML DOM 表示的构建器解析 XML 数据为 Document 对象:

XPathFactory xPathfactory = XPathFactory.newInstance(); 
XPath xpath = xPathfactory.newXPath(); 
XPathExpression xPathExpr = xpath.compile("/employees/employee");

设置一个 XPath 实例以查询 DOM:

NodeList nodes = (NodeList) xPathExpr.evaluate(doc, XPathConstants.NODESET);

配置 XPath 实例,编译选择 XML 文档中 employees 元素内所有 employee 元素的 XPath 表达式:

for (int i = 0; i < nodes.getLength(); i++) {
    Element node = (Element) nodes.item(i);
    Employee employee = new Employee();
    employee.setId(node.getElementsByTagName("id").item(0).getTextContent());
    employee.setFirstName(node.getElementsByTagName("firstName").item(0).getTextContent());
    employee.setLastName(node.getElementsByTagName("lastName").item(0).getTextContent());
    map.put(employee.getId(), employee);
}

评估 XPath 表达式在 doc 上,获取包含所有匹配员工元素的 NodeList

for (int i = 0; i < nodes.getLength(); i++) {
    Element node = (Element) nodes.item(i);
    Employee employee = new Employee();
    employee.setId(node.getElementsByTagName("id").item(0).getTextContent());
    employee.setFirstName(node.getElementsByTagName("firstName").item(0).getTextContent());
    employee.setLastName(node.getElementsByTagName("lastName").item(0).getTextContent());
    map.put(employee.getId(), employee);
}

遍历 NodeList 并将员工元素提取到 HashMap 中:

for (int i = 0; i < nodes.getLength(); i++) {
    Element node = (Element) nodes.item(i);
    Employee employee = new Employee();
    employee.setId(node.getElementsByTagName("id").item(0).getTextContent());
    employee.setFirstName(node.getElementsByTagName("firstName").item(0).getTextContent());
    employee.setLastName(node.getElementsByTagName("lastName").item(0).getTextContent());
    map.put(employee.getId(), employee);
}

7. 结论

在这篇文章中,我们探讨了将 XML 转换为 HashMap 的各种方法,这是一种存储键值对的基本数据结构。

XStream 和 Underscore 由于其简洁配置,适合于简单的 XML 解析。

Jackson 通过无缝映射 XML 元素,提供了灵活性和易用性。

JAXB 通过注解在需要标准化映射方法的场景中表现出色。

而 DOM 解析配合 XPath 提供了对 XML 元素的精细控制。

如往常一样,示例代码可在 GitHub 查看。