AI 摘要

文章系统梳理 XML 在 Java 框架中的基础应用:先解析 XML 语法、声明、元素、属性及 CDATA 特殊字符处理;继而对比 DOM、SAX、JDOM、DOM4J 四种解析方案;重点以示例演示 DOM 的增删改查全流程,并给出 DOM4J 的简洁写法,帮助开发者快速掌握 Java 对 XML 的读写与维护技巧。

XML 概述

XML 简介

XML 即可扩展标记语言,是一种简单的数据存储语言,使用一系列简单的标记来描述结构化数据。

XML 的特点:

  • XML 与操作系统、编程语言的开发平台都无关。
  • 规范统一,实现不同系统之间的数据交互。

XML 的作用:数据存储、数据交换、数据配置等。

XML 文档的基本结构:

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <!-- 图书信息 -->
    <book id="bk101">
        <title>.NET高级编程</title>
        <author>王姗</author>
        <description>包含C#框架和网络编程等</description>
    </book>
    <book id="bk102">
        <title>XML基础编程</title>
        <author>李明明</author>
        <description>包含XML基础概念和基本用法</description>
    </book>
</books>

XML 文档由声明、用于构建内容结构的元素、为元素补充特性的属性以及包含实际数据的文本内容等组成。

XML 声明

XML 声明即 XML 代码的第一行,用于标明该文件是一个 XML 文档,其定义了 XML 的版本和所使用的编码信息。

对于任何一个 XML 文档,其声明部分都是固定格式。

<?xml version="1.0" encoding="UTF-8"?>

XML 标签

在 XML 中,通过尖括号括起来的各种标签来标记数据,标签成对使用来界定字符数据。

XML 标签必须正确结束并且正确嵌套。

<元素名>...</元素名>

XML 元素

XML 元素由开始标签、结束标签和元素内容组成。

XML 元素的基本命名规则:

  • 名称中可以包含字母、数字或者其他的字符。
  • 名称不能以数字或者标点符号开始。
  • 名称不能以字符 xml、XML、Xml 开始。
  • 名称中不能含空格。

XML 根元素

每个 XML 文档必须有且只有一个根元素,根元素是一个完全包括文档中其他所有元素的元素。

<?xml version="1.0" encoding="UTF-8"?>
<book>
    <!-- ... -->
</book>

XML 属性

XML 元素可以指定属性信息。

<元素名 属性名="属性值" 属性名="属性值">...</元素名>

XML 特殊字符处理

在 XML 中,无法解析一些特殊字符,如“\<、\>、'、"、&”,在使用时可以对特殊字符进行转义。

在 XML 中解析特殊字符,还可以使用 CDATA 进行处理。

CDATA 的基本用法:

<![CDATA[...]]>

示例:XML 特殊字符处理

衣服尺码 XML 数据(classpath:ClothesSize.xml):

<?xml version="1.0" encoding="UTF-8"?>
<clothesSize>
    <size range="height&lt;165">S</size>
    <size range="165&lt;height&lt;170">M</size>
    <size range="110&lt;height&lt;175">L</size>
    <size range="175&lt;height&lt;180">XL</size>
    <size range="180&lt;height&lt;185">XXL</size>
</clothesSize>

XML 解析技术

目前主流的 XML 解析技术:

  • DOM:基于 XML 文档树结构来完成解析,适用于多次访问的 XML 文档,但是比较消耗资源。
  • SAX:基于事件的解析,适用于大数据量的 XML 文档,占用资源少,内存消耗小。
  • JDOM:针对 Java 的特定文档模型,API 大量使用了 Java 集合类型。
  • DOM4J:非常优秀的开源 Java XML API,比 JDOM 更加灵活,使用范围更广。

使用 DOM 解析 XML

DOM 概述

DOM 即文档对象模型(Document Object Model),DOM 把 XML 文件映射成一颗倒挂的 “树”,以根元素为根节点,每个节点都以对象的形式存在。

<book>
    <title>三国演义</title>
    <author>罗贯中</author>
    <price>30元</price>
</book>

DOM 结构:

DOM API 概述

在 Java 中,可以使用 Oracle 公司提供的 JAXP(Java API for XML Processing)来解析 XML。

JAXP 包含三个包,这三个包都在 JDK 中。

  • org.w3c.dom:W3C 推荐的用于使用 DOM 解析 XML 文档的接口。
  • org.xml.sax:用于使用 SAX 解析 XML 文档的接口。
  • javax.xml.parsers:解析器工厂工具,获得并配置特殊的分析器。

使用 DOM 操作 XML 时主要使用 Document、NodeList、Node、Element 对象。

Document 对象代表整个 XML 文档,它是 DOM 树的根节点,提供了访问和操作 XML 文档的入口点。

Document 对象的基本方法:

名称描述
NodeList getElementsByTagName(String tagName)根据标签名称返回节点列表
Element getDocumentElement()返回 XML 文档根元素对象
Element createElement(String tagName)创建元素

NodeList 对象是一个节点的集合。

NodeList 对象的基本方法:

名称描述
int getLength()返回节点集合的长度
Node item(int index)返回指定位置的节点对象

Node 对象是 DOM 树中的基本抽象节点类型,代表 XML 文档中的一个节点,可以是元素、属性、文本、注释、空白符等。

Node 对象的基本方法:

名称描述
NodeList getChildNodes()返回包含此节点所有子节点的节点集合
Node getFirstChild()返回第一个子节点
Node getLastChild()返回最后一个子节点
Node getNextSibling()返回当前节点的下一个兄弟节点
Node getPreviousSibling()返回当前节点的上一个兄弟节点
Node appendChild(Node child)将 child 节点追加到节点的末尾
String getNodeName()返回节点名称
String getNodeValue()返回节点的值
通常用于获取文本节点的文本值或属性节点的值,对元素节点返回 null
short getNodeType()返回节点的类型
String getTextContent()返回节点的值
通常用于获取元素节点及其子节点的所有文本内容
void setTextContent(String value)设置节点的值

Element 对象表示 XML 文档中的元素节点,Element 对象继承了 Node 对象。

通常在处理 XML 文档的结构和数据时,使用 Element 对象来操作具体的元素节点,进行数据提取、修改等操作。

Element 对象的基本方法:

名称描述
String getAttribute(String name)返回 name 属性的属性值
NodeList getElementsByTagName(String tagName)根据标签名称返回节点列表

使用 DOM 读取数据

在 Java 中使用 DOM 解析 XML 文档的基本步骤:

  • 创建解析器工厂 DocumentBuilderFactory 对象。
  • 由解析器工厂对象创建解析器 DocumentBuilder 对象。
  • 由解析器对象对 XML 文件进行解析、构建 DOM 树,创建相应的 Document 对象。
  • 以 Document 对象为起点对 DOM 树的节点进行增加、删除、修改、查询等操作。

示例:使用 DOM 读取数据

手机 XML 数据(classpath:phone.xml):

<?xml version="1.0" encoding="UTF-8"?>
<PhoneInfo>
    <Brand name="华为">
        <type name="Mate X5">
            <price>14999.00</price>
        </type>
        <type name="Mate 60">
            <price>8999.00</price>
        </type>
    </Brand>
    <Brand name="苹果">
        <type name="iPhone 15">
            <price>9999.00</price>
        </type>
        <type name="iPhone 14">
            <price>7999.00</price>
        </type>
    </Brand>
</PhoneInfo>

测试类(cn.duozai.TestMain):

public class TestMain {

    public static void main(String[] args) throws Exception {
        // 读取并解析XML
        // 创建解析器工厂DocumentBuilderFactory对象
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        // 创建解析器DocumentBuilder对象
        DocumentBuilder db = dbf.newDocumentBuilder();
        // 解析XML文档
        Documentdocument = db.parse("resources/phone.xml");

        // 获取手机品牌Brand节点列表
        NodeList brandList = document.getElementsByTagName("Brand");

        // 遍历手机品牌Brand节点列表
        for (int i = 0; i < brandList.getLength(); i++) {
            // 获取遍历的手机品牌Brand节点对象
            Node brandNode = brandList.item(i);
            // 将手机品牌Brand节点对象转换成元素对象
            Element brandElement = (Element) brandNode;
            // 获取手机品牌Brand元素对象的name属性的值
            String brandName = brandElement.getAttribute("name");
            // 获取手机品牌Brand元素对象的手机型号type子节点列表
            NodeList typeList = brandElement.getElementsByTagName("type");
            // 遍历手机型号type节点列表
            for (int j = 0; j < typeList.getLength(); j++) {
                // 获取遍历的手机型号type节点对象
                Node typeNode = typeList.item(j);
                // 将手机型号type节点对象转换成元素对象
                Element typeElement = (Element) typeNode;
                // 获取手机型号type元素对象的name属性的值
                String typeName = typeElement.getAttribute("name");
                // 获取手机型号type元素对象的型号价格price子节点列表
                NodeList priceNodeList = typeElement.getElementsByTagName("price");
                // 将第一个型号价格price节点对象转换成元素对象
                Element priceElement = (Element) priceNodeList.item(0);
                // 获取型号价格price元素对象的文本内容
                String price = priceElement.getTextContent();

                // 输出手机品牌名称+手机型号名称+型号价格
                System.out.println(brandName + typeName + "的价格是" + price);
            }
        }
    }

}

示例效果:


使用 DOM 添加数据

使用 DOM 添加数据的基本步骤:

  • 为 XML 文档构建 DOM 树。
  • 创建新节点,并设置节点属性。
  • 把节点添加到其所属父节点上。
  • 保存 XML 文档。

保存 XML 文档的基本步骤:

  • 获得 TransformerFactory 对象。
  • 通过 TransformerFactory 对象创建 Transformer 对象。
  • 创建 DOMSource 对象,将 Document 对象转换成 DOMSource 对象。
  • 创建 StreamResult 对象和 FileOutputStream 对象,包含保存文件的信息。
  • 调用 Transformer 对象中的方法将 XML 保存到指定文件中。

示例:使用 DOM 添加数据

测试类(cn.duozai.TestMain):

public class TestMain {

    public static void main(String[] args) throws Exception {
        // 读取并解析XML
        // ...

        // 创建手机品牌Brand元素对象
        Element brandElement = document.createElement("Brand");
        // 设置手机品牌Brand元素对象的name属性
        brandElement.setAttribute("name", "小米");

        // 创建手机型号type元素对象
        Element mi15Element = document.createElement("type");
        // 设置手机型号type元素对象的name属性
        mi15Element.setAttribute("name", "Mi 15");

        // 创建型号价格price元素对象
        Element priceElement = document.createElement("price");
        // 设置型号价格price元素对象的文本内容
        priceElement.setTextContent("4999");

        // 将型号价格price元素对象追加到手机型号type元素对象中
        mi15Element.appendChild(priceElement);

        // 将手机型号type元素对象追加到手机品牌Brand元素对象
        brandElement.appendChild(mi15Element);

        // 将手机品牌Brand元素对象追加到根节点PhoneInfo中
        NodeList phoneInfoList = document.getElementsByTagName("PhoneInfo");
        Node phoneInfo = phoneInfoList.item(0);
        phoneInfo.appendChild(brandElement);

        // 保存XML
        // 创建转换器工厂TransformerFactory
        TransformerFactory tff = TransformerFactory.newInstance();
        // 创建转换器对象Transformer
        Transformer tf = tff.newTransformer();
        // 创建DOMSource对象,将文档对象转换成DOM源对象
        DOMSource domSource = new DOMSource(document);
        // 创建输出流对象
        StreamResult sr = new StreamResult(new FileOutputStream("resources/phone.xml"));
        // 将DOM源对象转换成文件
        tf.transform(domSource, sr);
    }

}

示例效果:


使用 DOM 修改数据

使用 DOM 修改数据的基本步骤:

  • 为 XML 文档构建 DOM 树。
  • 找到符合修改条件的节点。
  • 设置该节点的属性为修改值。
  • 保存 XML 文档。

示例:使用 DOM 修改数据

测试类(cn.duozai.TestMain):

public class TestMain {

    public static void main(String[] args) throws Exception {
        // 读取并解析XML
        // ...

        // 获取手机品牌Brand节点列表
        NodeList nodeList = document.getElementsByTagName("Brand");
        // 遍历手机品牌Brand节点列表
        for (int i = 0; i < nodeList.getLength(); i++) {
            // 获取遍历的手机品牌Brand节点对象
            // 将手机品牌Brand节点对象转换成元素对象
            Element element = (Element) nodeList.item(i);

            // 如果手机品牌Brand元素对象的name属性的值等于华为,则修改其属性值
            if ("华为".equals(element.getAttribute("name"))) {
                element.setAttribute("name", "HUAWEI");
            }
        }

        // 保存XML
        // ...
    }

}

示例效果:


使用 DOM 删除数据

使用 DOM 删除数据的基本步骤:

  • 为 XML 文档构建 DOM 树。
  • 找到符合删除条件的节点。
  • 获取该节点的父节点,从父节点中删除该节点。
  • 保存 XML 文档。

示例:使用 DOM 删除数据

测试类(cn.duozai.TestMain):

public class TestMain {

    public static void main(String[] args) throws Exception {
        // 读取并解析XML
        // ...

        // 获取手机品牌Brand节点列表
        NodeList nodeList = document.getElementsByTagName("Brand");
        // 遍历手机品牌Brand节点列表
        for (int i = 0; i < nodeList.getLength(); i++) {
            // 获取遍历的手机品牌Brand节点对象
            // 将手机品牌Brand节点对象转换成元素对象
            Element element = (Element) nodeList.item(i);

            // 如果手机品牌Brand元素对象的name属性的值等于苹果,则删除该元素对象
            if ("苹果".equals(element.getAttribute("name"))) {
                // 获取当前元素对象的父节点对象,再从父节点对象中把当前元素对象移除
                element.getParentNode().removeChild(element);
            }
        }

        // 保存XML
        // ...
    }

}

示例效果:


使用 DOM4J 解析 XML

DOM4J API 概述

DOM4J 是一个易用、功能强大且开源的 Java XML 处理 API。

使用 DOM4J 操作 XML 时主要使用 Document、Element、Branch、Attribute 对象。

Document 对象代表整个 XML 文档,是 DOM4J 中操作 XML 文档的入口点。

Document 对象的基本方法:

名称描述
Element getRootElement()获取文档的根元素

Element 对象对应 XML 文档中的具体元素标签。

Element 对象的基本方法:

名称描述
Element element(String name)根据节点名称获取某个节点的单个子节点
List elements()获取某个节点的子节点列表
List elements(String name)根据节点名称获取某个节点的子节点列表
Attribute attribute(String name)根据属性名获取某个节点下的属性
String attributeValue(String name)根据属性名获取某个节点下的属性值
Element addAttribute(String name, String value)为某个节点添加属性
String getText()获取节点文字
void setText(String value)设置节点文字

Branch 对象是 Element 对象和 Document 对象的抽象父类,定义了一些公共的、用于处理树形结构节点相关的通用方法。

Branch 对象的基本方法:

名称描述
Element addElement(String name)在某个节点下添加子节点
boolean remove(Node node)从父节点中删除某个子节点

Attribute 对象表示 XML 元素的属性。

Attribute 对象的基本方法:

名称描述
String getText()获取属性的值
void setText(String value)设置属性的值

使用 DOM4J 操作数据

使用 DOM4J 操作 XML 的基本步骤:

  • 下载 dom4j 依赖 jar 文件:https://dom4j.github.io/
  • 在项目中导入 dom4j 依赖 jar 文件。
  • 创建 SAXReader 对象读取 XML 文件。
  • 调用 Document、Element 等对象的相关方法操作 XML。

示例:使用 DOM4J 操作数据

导入相关依赖 JAR 文件,并添加为库。

示例效果:

测试类(cn.duozai.TestMain):

public class TestMain {

    public static void main(String[] args) throws Exception {
        // 创建SAXReader对象读取XML
        SAXReader reader = new SAXReader();
        Document document = reader.read("resources/phone.xml");

        // 获取根元素对象
        Element rootElement = document.getRootElement();

        // 获取手机品牌Brand子元素列表
        List<Element> brandList = rootElement.elements("Brand");
        // 遍历手机品牌Brand子元素列表
        for (Element brandElement : brandList) {
            // 获取手机品牌Brand元素对象的name属性的值
            String brandName = brandElement.attributeValue("name");
            // 获取手机品牌Brand元素对象的手机型号type子元素列表
            List<Element> typeList = brandElement.elements("type");
            // 手机型号type元素列表
            for (Element typeElement : typeList) {
                // 获取手机型号type元素对象的name属性的值
                String typeName = typeElement.attributeValue("name");
                // 获取手机型号type元素对象的型号价格price子元素对象
                Element priceElement = typeElement.element("price");
                // 获取型号价格price元素对象的文本内容
                String price = priceElement.getText();

                // 输出品牌名称+手机类型+价格
                // 输出手机品牌名称+手机型号名称+型号价格
                System.out.println(brandName + typeName + "的价格是" + price);
            }
        }
    }

}

示例效果: