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为例。

<?xml version="1.0" encoding="UTF-8"?>
<employee>
    <employee_list>
        <employee ID="1">
            <firstname>Andrei</firstname>
            <lastname>Rus</lastname>
            <age>23</age>
            <position-skill ref="Java"/>
            <detail-ref ref="AndreiR"/>
        </employee>
        <!-- ... 其他员工 ... -->
    </employee_list>

    <position_details>
        <position ID="Java">
            <role>Junior Developer</role>
            <skill_name>Java</skill_name>
            <experience>1</experience>
        </position>
        <!-- ... 其他职位 ... -->
    </position_details>

    <employee_info>
        <detail ID="AndreiR">
            <username>AndreiR</username>
            <residence>Timisoara</residence>
            <yearOfBirth>1999</yearOfBirth>
            <phone>0</phone>
        </detail>
        <!-- ... 其他详情 ... -->
    </employee_info>
</employee>

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

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

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

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

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

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

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

DreamStudio
DreamStudio

SD兄弟产品!AI 图像生成器

下载

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

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

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

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<String, PositionDetails> 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<String, EmployeeInfo> 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<Person> 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"),这确保了搜索范围被限定在 <position_details> 标签内部。
  4. 关联数据与构建 Person 对象:
    • 在解析 employee_list 时,我们遍历每个 <employee> 元素。
    • 从 <employee> 元素中提取其直接属性(如 ID、firstname、lastname、age)。
    • 获取 position-skill 和 detail-ref 元素的 ref 属性值。这些 ref 值是连接不同数据部分的“桥梁”。
    • 使用这些 ref 值作为键,从之前构建的 positionDetailsMap 和 employeeInfoMap 中查找对应的详情数据。
    • 将所有相关数据整合到一个 Person 对象中,并添加到 List<Person> 中。
  5. 统一输出: 最后,遍历 List<Person>,按照预设的格式打印每个 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解析任务。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

338

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

225

2025.10.31

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

138

2026.02.12

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

254

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1110

2024.03.01

pdf怎么转换成xml格式
pdf怎么转换成xml格式

将 pdf 转换为 xml 的方法:1. 使用在线转换器;2. 使用桌面软件(如 adobe acrobat、itext);3. 使用命令行工具(如 pdftoxml)。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1949

2024.04.01

xml怎么变成word
xml怎么变成word

步骤:1. 导入 xml 文件;2. 选择 xml 结构;3. 映射 xml 元素到 word 元素;4. 生成 word 文档。提示:确保 xml 文件结构良好,并预览 word 文档以验证转换是否成功。想了解更多xml的相关内容,可以阅读本专题下面的文章。

2119

2024.08.01

xml是什么格式的文件
xml是什么格式的文件

xml是一种纯文本格式的文件。xml指的是可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。想了解更多相关的内容,可阅读本专题下面的相关文章。

1172

2024.11.28

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

49

2026.03.13

热门下载

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

精品课程

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

共23课时 | 4.4万人学习

C# 教程
C# 教程

共94课时 | 11.4万人学习

Java 教程
Java 教程

共578课时 | 82.3万人学习

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

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