0

0

Java DOM解析多层XML文件并实现数据关联与分组输出

聖光之護

聖光之護

发布时间:2025-11-26 14:47:02

|

504人浏览过

|

来源于php中文网

原创

java dom解析多层xml文件并实现数据关联与分组输出

本文详细介绍了如何使用Java DOM解析器处理具有多层结构的XML文件,特别关注了getElementsByTagName方法的正确使用以避免全局搜索问题。教程将指导读者如何将不同XML节点中的关联数据进行整合,并通过自定义Java对象实现结构化的数据存储和分组输出,最终呈现一个完整且可读性强的解析方案。

1. 理解XML结构与DOM解析基础

在处理复杂的XML文件时,首先要清晰地理解其结构。本教程以一个包含员工列表(employee_list)、职位详情(position_details)和员工信息(employee_info)的多层XML为例。



    
        
            Andrei
            Rus
            23
            
            
        
        
    

    
        
            Junior Developer
            Java
            1
        
        
    

    
        
            AndreiR
            Timisoara
            1999
            0
        
        
    

Java DOM(Document Object Model)解析器将整个XML文档加载到内存中,并将其表示为一棵节点树。这使得开发者可以通过遍历树结构来访问和操作XML数据。核心类包括DocumentBuilderFactory、DocumentBuilder和Document。

2. 初始解析尝试与常见问题

在使用DOM解析时,一个常见的陷阱是Document.getElementsByTagName()方法的全局搜索特性。它会在整个文档中查找所有匹配指定标签名的元素,而不管它们在DOM树中的具体位置。

立即学习Java免费学习笔记(深入)”;

例如,如果直接使用 doc.getElementsByTagName("employee"),它不仅会找到 employee_list 下的 元素,还会意外地匹配到根元素 本身。由于根元素没有 ID 等子元素,后续尝试获取这些属性或子节点时可能会导致错误或空指针异常。

// 初始尝试可能导致的问题代码片段
NodeList nList = doc.getElementsByTagName("employee"); // 可能会包含根元素
// ... 遍历nList时,第一个元素可能是根元素,导致后续getAttribute("id")等操作失败

为了避免这个问题,我们需要更精确地限定搜索范围。

Elser AI Comics
Elser AI Comics

一个免费且强大的AI漫画生成工具,助力你三步创作自己的一出好戏

下载

3. 精确限定搜索范围的解析策略

正确的做法是,首先定位到包含目标元素的父节点,然后在该父节点下进行局部搜索。

例如,要获取所有员工信息,应首先找到 节点,然后在其内部查找所有的 节点。

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;

public class XmlParserTutorial {

    // 定义用于存储职位信息的内部类
    static class PositionDetails {
        String id;
        String role;
        String skillName;
        int experience;

        public PositionDetails(String id, String role, String skillName, int experience) {
            this.id = id;
            this.role = role;
            this.skillName = skillName;
            this.experience = experience;
        }
        // Getters
        public String getId() { return id; }
        public String getRole() { return role; }
        public String getSkillName() { return skillName; }
        public int getExperience() { return experience; }
    }

    // 定义用于存储员工详细信息的内部类
    static class EmployeeInfo {
        String id;
        String username;
        String residence;
        int yearOfBirth;
        String phone;

        public EmployeeInfo(String id, String username, String residence, int yearOfBirth, String phone) {
            this.id = id;
            this.username = username;
            this.residence = residence;
            this.yearOfBirth = yearOfBirth;
            this.phone = phone;
        }
        // Getters
        public String getId() { return id; }
        public String getUsername() { return username; }
        public String getResidence() { return residence; }
        public int getYearOfBirth() { return yearOfBirth; }
        public String getPhone() { return phone; }
    }

    // 定义用于存储完整员工数据的POJO
    static class Person {
        String id;
        String firstName;
        String lastName;
        int age;
        String role;
        String skillName;
        int experience;
        String username;
        String residence;
        int yearOfBirth;
        String phone;

        // Getters and Setters (省略,为简洁起见)
        public String getId() { return id; }
        public String getFirstName() { return firstName; }
        public String getLastName() { return lastName; }
        public int getAge() { return age; }
        public String getRole() { return role; }
        public String getSkillName() { return skillName; }
        public int getExperience() { return experience; }
        public String getUsername() { return username; }
        public String getResidence() { return residence; }
        public int getYearOfBirth() { return yearOfBirth; }
        public String getPhone() { return phone; }

        @Override
        public String toString() {
            return "PersonId: " + id + "\n" +
                   "  firstname: " + firstName + "\n" +
                   "  lastname: " + lastName + "\n" +
                   "  age: " + age + "\n" +
                   "  role: " + role + "\n" +
                   "  skill_name: " + skillName + "\n" +
                   "  experience: " + experience + "\n" +
                   "  username: " + username + "\n" +
                   "  residence: " + residence + "\n" +
                   "  yearOfBirth: " + yearOfBirth + "\n" +
                   "  phone: " + phone + "\n";
        }
    }

    public static void main(String[] args) {
        try {
            File xmlDoc = new File("employees.xml"); // 确保XML文件名为employees.xml
            DocumentBuilderFactory dbFact = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuild = dbFact.newDocumentBuilder();
            Document doc = dBuild.parse(xmlDoc);

            doc.getDocumentElement().normalize(); // 规范化文档,处理空白文本节点

            System.out.println("Root element: " + doc.getDocumentElement().getNodeName());
            System.out.println("-----------------------------------------------------------------------------");

            // 1. 解析 position_details 并存储到Map中
            Map positionDetailsMap = new HashMap<>();
            NodeList positionListNodes = doc.getElementsByTagName("position_details");
            if (positionListNodes.getLength() > 0) {
                Element positionDetailsElement = (Element) positionListNodes.item(0);
                NodeList positions = positionDetailsElement.getElementsByTagName("position");
                for (int i = 0; i < positions.getLength(); i++) {
                    Node positionNode = positions.item(i);
                    if (positionNode.getNodeType() == Node.ELEMENT_NODE) {
                        Element positionElement = (Element) positionNode;
                        String id = positionElement.getAttribute("ID");
                        String role = getElementTextContent(positionElement, "role");
                        String skillName = getElementTextContent(positionElement, "skill_name");
                        int experience = Integer.parseInt(getElementTextContent(positionElement, "experience"));
                        positionDetailsMap.put(id, new PositionDetails(id, role, skillName, experience));
                    }
                }
            }

            // 2. 解析 employee_info 并存储到Map中
            Map employeeInfoMap = new HashMap<>();
            NodeList employeeInfoListNodes = doc.getElementsByTagName("employee_info");
            if (employeeInfoListNodes.getLength() > 0) {
                Element employeeInfoElement = (Element) employeeInfoListNodes.item(0);
                NodeList details = employeeInfoElement.getElementsByTagName("detail");
                for (int i = 0; i < details.getLength(); i++) {
                    Node detailNode = details.item(i);
                    if (detailNode.getNodeType() == Node.ELEMENT_NODE) {
                        Element detailElement = (Element) detailNode;
                        String id = detailElement.getAttribute("ID");
                        String username = getElementTextContent(detailElement, "username");
                        String residence = getElementTextContent(detailElement, "residence");
                        int yearOfBirth = Integer.parseInt(getElementTextContent(detailElement, "yearOfBirth"));
                        String phone = getElementTextContent(detailElement, "phone");
                        employeeInfoMap.put(id, new EmployeeInfo(id, username, residence, yearOfBirth, phone));
                    }
                }
            }

            // 3. 解析 employee_list 并关联数据
            List people = new ArrayList<>();
            NodeList employeeListNodes = doc.getElementsByTagName("employee_list");
            if (employeeListNodes.getLength() > 0) {
                Element employeeListElement = (Element) employeeListNodes.item(0);
                NodeList employees = employeeListElement.getElementsByTagName("employee");
                System.out.println("Total Employees found: " + employees.getLength());
                System.out.println("-----------------------------------------------------");

                for (int i = 0; i < employees.getLength(); i++) {
                    Node employeeNode = employees.item(i);
                    if (employeeNode.getNodeType() == Node.ELEMENT_NODE) {
                        Element employeeElement = (Element) employeeNode;

                        Person person = new Person();
                        person.id = employeeElement.getAttribute("ID");
                        person.firstName = getElementTextContent(employeeElement, "firstname");
                        person.lastName = getElementTextContent(employeeElement, "lastname");
                        person.age = Integer.parseInt(getElementTextContent(employeeElement, "age"));

                        // 获取关联引用
                        String positionSkillRef = ((Element) employeeElement.getElementsByTagName("position-skill").item(0)).getAttribute("ref");
                        String detailRef = ((Element) employeeElement.getElementsByTagName("detail-ref").item(0)).getAttribute("ref");

                        // 从Map中获取关联数据
                        PositionDetails pos = positionDetailsMap.get(positionSkillRef);
                        if (pos != null) {
                            person.role = pos.getRole();
                            person.skillName = pos.getSkillName();
                            person.experience = pos.getExperience();
                        }

                        EmployeeInfo empInfo = employeeInfoMap.get(detailRef);
                        if (empInfo != null) {
                            person.username = empInfo.getUsername();
                            person.residence = empInfo.getResidence();
                            person.yearOfBirth = empInfo.getYearOfBirth();
                            person.phone = empInfo.getPhone();
                        }
                        people.add(person);
                    }
                }
            }

            // 4. 输出分组后的数据
            System.out.println("\n=============================================================================================");
            System.out.println("Grouped Employee Data:");
            System.out.println("=============================================================================================");
            for (Person p : people) {
                System.out.println(p);
                System.out.println("--------------------------------------------------------------------------");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 辅助方法:获取指定父元素下某个标签的文本内容
     * @param parentElement 父元素
     * @param tagName 标签名
     * @return 标签的文本内容,如果不存在则返回空字符串
     */
    private static String getElementTextContent(Element parentElement, String tagName) {
        NodeList nodeList = parentElement.getElementsByTagName(tagName);
        if (nodeList != null && nodeList.getLength() > 0) {
            return nodeList.item(0).getTextContent();
        }
        return "";
    }
}

代码说明:

  1. 辅助类定义: PositionDetails、EmployeeInfo 和 Person 类用于封装不同类别的数据。Person 类是最终需要输出的完整数据结构。
  2. doc.getDocumentElement().normalize(): 这是一个重要的步骤,用于清理DOM树中的空白文本节点,确保解析结果的稳定性。
  3. 分步解析与数据存储:
    • 首先,我们独立解析 position_details 和 employee_info 两个部分。
    • 将解析出的数据存储到 Map 中,其中键是元素的 ID 属性(如 position ID="Java" 或 detail ID="AndreiR"),值是对应的 PositionDetails 或 EmployeeInfo 对象。这种方式便于后续通过引用快速查找。
    • 注意:doc.getElementsByTagName("position_details") 仍然是全局搜索,但由于 position_details 在文档中是唯一的,所以 item(0) 是安全的。然后,我们在 positionDetailsElement 内部调用 getElementsByTagName("position"),这确保了搜索范围被限定在 标签内部。
  4. 关联数据与构建 Person 对象:
    • 在解析 employee_list 时,我们遍历每个 元素。
    • 元素中提取其直接属性(如 ID、firstname、lastname、age)。
    • 获取 position-skill 和 detail-ref 元素的 ref 属性值。这些 ref 值是连接不同数据部分的“桥梁”。
    • 使用这些 ref 值作为键,从之前构建的 positionDetailsMap 和 employeeInfoMap 中查找对应的详情数据。
    • 将所有相关数据整合到一个 Person 对象中,并添加到 List 中。
  5. 统一输出: 最后,遍历 List,按照预设的格式打印每个 Person 对象的详细信息。Person 类的 toString() 方法被重写以提供美观的输出格式。
  6. getElementTextContent 辅助方法: 这个方法封装了获取子元素文本内容的逻辑,避免了重复代码和潜在的 NullPointerException(当子元素不存在时)。

4. 注意事项与最佳实践

  • 错误处理: 示例代码中使用了简单的 try-catch(Exception e)。在生产环境中,应进行更细粒化的异常处理,例如 ParserConfigurationException, SAXException, IOException 等。
  • XML文件路径: 确保 new File("employees.xml") 中的文件路径正确。如果文件不在项目根目录下,需要提供完整路径或相对路径。
  • 性能考量: DOM解析器会将整个XML文档加载到内存中。对于非常大的XML文件(几十MB甚至GB级别),这可能会导致内存溢出。在这种情况下,SAX(Simple API for XML)或StAX(Streaming API for XML)解析器可能更适合,它们以流式方式处理XML,占用内存较少。
  • XPath: 对于更复杂的查询,例如查找具有特定属性值的元素,或者跨层级查找,XPath(XML Path Language)是比 getElementsByTagName 更强大和灵活的工具。Java提供了 javax.xml.xpath 包来支持XPath。
  • 空值处理: 在获取元素文本内容或属性时,始终要考虑元素或属性可能不存在的情况。示例中的 getElementTextContent 方法对此进行了初步处理,但更健壮的代码应包含更多 null 检查。
  • 数据类型转换: 从XML中读取的数据通常是字符串,需要根据实际数据类型进行转换(如 Integer.parseInt())。务必处理好 NumberFormatException。

5. 总结

通过本教程,我们学习了如何使用Java DOM解析器处理具有复杂层级和数据关联的XML文件。关键在于:

  1. 精确限定搜索范围:避免 Document.getElementsByTagName() 的全局搜索问题,通过在父元素内部进行局部搜索来获取目标节点。
  2. 数据结构化与关联:利用Java对象(POJO)来封装XML数据,并通过 Map 等数据结构实现不同节点间的数据关联和快速查找。
  3. 统一输出:将解析后的数据存储在集合中,然后统一遍历输出,以实现更清晰、更符合业务逻辑的数据呈现。

掌握这些技巧将有助于您更高效、更健壮地处理各种XML解析任务。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

831

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

737

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

733

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16925

2023.08.03

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

3

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.6万人学习

Java 教程
Java 教程

共578课时 | 45.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号