从零开始学Java之带你学会解析XML文件

作者 :孙玉昌,昵称【一一哥 】,另外【壹壹哥】也是我哦

千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者

前言

经过前面100多篇文章的介绍,壹哥 其实已经把Java中的基础内容给大家介绍完毕了。但为了让大家尽可能多得掌握一些技能,提高一些竞争力,壹哥 打算再利用几篇文章来给大家介绍一下如何进行信息的承载和传输。在开发时,能够实现信息承载和传输的技术有很多,比如XML、JSON、properties、yml等格式的文件。实际上,这些文件其实并不专属于Java,它们是独立于某一种开发语言的,属于一种单独的技术范畴。在接下来的几篇文章中,壹哥会给大家简单普及XML及JSON解析的内容,为我们后面的开发建立起必要的知识储备。Let's Go!XML解析搞起来。

------------------------------前戏已做完,精彩即开始----------------------------

全文大约【5300】 字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图视频,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考......

配套开源项目资料

Github: github.com/SunLtd/Lear...

Gitee: gitee.com/sunyiyi/Lea...

一. XML简介

1. 概念

XML(Extensible Markup Language,可扩展的标记语言) ,作为一种标记语言,它是标准通用标记语言的子集。XML给我们提供了可扩展性和自定义标签的特性,允许用户对自己的标记语言进行定义。同时XML也是一种常用的数据交换格式,可以用来标记数据、定义数据类型,使得它成为了Web开发中最常用的数据格式之一。

当然上面的概念比较抽象,作为初学者,你可能不太好理解。实际上,XML类似于HTML,它俩可以说是"堂兄弟",两者都是标记语言。但与HTML不同的是,XML是可扩展的,这意味着用户可以定义自己的标记和元素,并且XML的设计目的是传输和存储数据,这就使得XML成了一种极为灵活的数据交换格式。

2. 使用场景

由于XML具有良好的可扩展性、内容与形式分离、遵循严格的语法要求、保值性良好等优点,所以XML文档可以应用于很多场景中,比如:

  • 数据交换:XML可以用来标记数据、定义数据类型,使得它成为了Web开发中最常用的数据格式之一。
  • 网络传输:XML文档可以通过HTTP协议在网络上传输,从而实现分布式应用程序之间的通信。
  • 配置文件:很多软件和系统使用XML文档作为配置文件,以便用户可以通过修改XML文件来改变软件和系统的行为。
  • 模板语言:XML可以用作模板语言(类似于HTML),用于生成动态的Web页面和报告。
  • 数据库存储:一些数据库系统可以将XML文档存储为BLOB(二进制大对象),从而实现对XML文档的存储和查询。

3. 文档构成

我们一般是在一个.xml格式的文档中操作XML,而一个XML文档主要由标签、元素和属性组成。标签是XML文档中的基本单位,它们用来表示数据的开始和结束位置,"<>"是开始标签,"</>"是结束标签。元素是标签中包含的内容,它们可以包含其他的元素或文本内容。属性是元素的附加信息,它们定义了一个元素的特性和属性。我们来看看下面这个简单的XML文档:

xml 复制代码
<bookstore>
  <book category="children">
    <title lang="en">跟壹哥学java</title>
    <author>一一哥</author>
    <year>2008</year>
    <price>29.99</price>
  </book>
  <book category="web">
    <title lang="en">跟壹哥学XML</title>
    <author>壹哥</author>
    <year>2023</year>
    <price>39.88</price>
  </book>
</bookstore>

在这个XML文档中,bookstore是一个元素,它包含了两个book元素。每个book元素都有一个category属性,表示书籍的类别。每个book元素中又包含了title、author、year和price四个子元素。title元素中还有一个lang属性,表示书籍的语言。

4. 文档规范

XML文档必须符合XML规范,包括正确的标记语法、正确的嵌套结构、正确的属性格式等。一个XML文档必须有一个根元素,所有其他元素都必须是这个根元素的子元素。XML元素和属性的名称是区分大小写的。XML文档中可以包含注释和处理指令。XML文档中可以使用实体引用来表示特殊字符,比如<表示小于号,>表示大于号。具体来说,有以下这些XML规范要求需要我们遵守:

  1. 正确的标记语法:XML文档中的所有元素和属性都必须使用正确的标记语法,即开始标记和结束标记之间不能有其他标记或文本。
  2. 正确的嵌套结构:XML文档中的元素必须按照正确的嵌套结构进行排列,即一个元素不能包含另一个元素的开始标记和结束标记,也不能与另一个元素的开始标记和结束标记重叠。
  3. 正确的属性格式:XML文档中的属性必须使用正确的格式进行定义,即属性名和属性值之间必须有一个等号,并且属性值必须使用引号括起来。
  4. 有且仅有一个根元素:XML文档中必须有一个根元素,所有其他元素都必须是这个根元素的子元素。
  5. 元素和属性名称区分大小写:XML元素和属性的名称是区分大小写的。
  6. 注释和处理指令:XML文档中可以包含注释和处理指令。
  7. 实体引用:XML文档中可以使用实体引用来表示特殊字符,比如<表示小于号,>表示大于号。

以上规范是XML文档必须遵循的基本要求,只有满足这些要求,才能保证XML文档的正确性和可读性。

5. 注意事项

虽然XML文档操作时比较灵活自由,但我们在使用XML文档时,也需要注意以下几点:

  • XML文档必须符合XML规范,包括正确的标记语法、正确的嵌套结构、正确的属性格式等;
  • XML文档必须有一个根元素,所有其他的元素都必须是这个根元素的子元素;
  • XML元素和属性的名称是严格区分大小写的;
  • XML文档中可以包含注释和处理指令
  • XML文档中可以使用实体引用来表示特殊字符,比如<表示小于号,>表示大于号。

我们在开发时一定要注意XML文档的这些规范要求,否则可能会出现一些莫名其妙的错误。

二. XML解析

1. 解析概念

XML解析就是将XML文档转换为可操作的数据结构的过程。XML解析器读取XML文档并将其转换为内存中的树形结构,然后再通过某种编程语言的API来访问和操作这个树形结构,得到树形结构中的数据信息。

2. 解析方式

目前在XML解析方式中,有四种常用的解析技术:DOM、SAX、StAX和Pull

2.1 DOM

DOM(文档对象模型)是一种基于树形结构的XML解析技术。它会把整个XML文档转换为一个树形结构,并将每个元素都表示为一个节点。我们通过遍历这个树形结构,就可以访问和操作XML文档中的任何元素和属性。

虽然DOM解析给我们提供了非常方便的操作API,但它需要将整个XML文档都加载到内存中,因此对于大型XML文件来说,DOM的性能和内存占用都会比较高,对内存消耗比较大,所以现在开发时较少使用,尤其是在移动端。

2.2 SAX

SAX(简单API for XML)是一种基于事件的XML解析技术。SAX解析器读取XML文档,并可以在解析过程中触发一系列的事件。应用程序可以注册回调函数来处理这些事件,从而读取XML文档中的元素和属性。

与DOM相比,SAX是一种流式解析技术,它不需要将整个XML文档加载到内存中,因此对于大型的XML文件来说,SAX的性能和内存占用都会比较低。在移动端开发中,该方式较为常用。

2.3 StAX

StAX(流式API for XML)是一种基于迭代器的XML解析技术。StAX解析器读取XML文档,并返回一个迭代器,应用程序可以使用这个迭代器来遍历XML文档中的元素和属性。

与DOM和SAX相比,StAX的API更加的简单和直观,同时也不需要将整个XML文档加载到内存中,因此对于大型XML文件来说,StAX的性能和内存占用都会比较低。

2.4 Pull解析

Pull解析也是一种基于迭代器的XML解析技术,它与StAX类似,但更加简洁和易用。Pull解析器读取XML文档后会返回一个迭代器,应用程序可以使用这个迭代器来遍历XML文档中的元素和属性。

与DOM和SAX相比,Pull解析器的API更加简单和直观,同时也不需要将整个XML文档加载到内存中,因此对于大型XML文件来说,Pull解析器的性能和内存占用都会比较低。在移动端开发中,Pull解析很常用。

因为现在XML解析并不是很常用,所以壹哥并不会把以上4种解析方式,都带大家进行代码实现,我会挑选其中的DOM和Pull解析进行代码讲解。

3. DOM解析

我们在Java中进行DOM解析时,需要依赖javax.xml.parsers包中的DocumentBuilder类和Document类,这两个类都是Java标准库中的一部分,因此无需额外下载依赖包。

但如果我们需要使用更高级的XML解析特性,比如XPath、XSLT、DTD、Schema等,可以考虑使用第三方的XML处理库,比如Apache Xerces、JDOM、DOM4J等开源框架。这些库给我们提供了更多的XML解析和处理API,同时也提供了更好的性能和更丰富的特性,我们可以在它们的官方网站下载和使用这些库。

接下来壹哥 就通过一个案例,来带大家利用DOM进行XML解析。在解析XML时,我们可以在项目中提前准备一个XML文件,存放到项目的文件路径中。壹哥就把上面的bookstore的xml文件,存放在了src目录下,如下图所示:

接着我们就可以加载这个xml文件,并对其进行解析了,代码如下:

java 复制代码
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * @author 一一哥Sun
 * @company 千锋教育
 */
public class Demo01 {
    public static void main(String[] args) {
        try {
            // 创建DOM解析器工厂
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            // 创建DOM解析器
            DocumentBuilder builder = factory.newDocumentBuilder();
            // 加载XML文件,解析XML文档
            Document doc = builder.parse(new File("src/books.xml"));

            // 获取根元素
            Element root = doc.getDocumentElement();
            System.out.println("根元素:" + root.getNodeName());

            // 获取所有的book元素
            NodeList books = root.getElementsByTagName("book");
            for (int i = 0; i < books.getLength(); i++) {
                //得到得到每一个book元素
                Element book = (Element) books.item(i);
                System.out.println("book元素:");

                // 获取book元素中的category属性
                String category = book.getAttribute("category");
                System.out.println("category属性:" + category);

                // 获取title元素
                Element title = (Element) book.getElementsByTagName("title").item(0);
                //获取title里的lang属性
                String lang = title.getAttribute("lang");
                //得到元素中的第一个孩子的节点内容
                String text = title.getFirstChild().getNodeValue();
                System.out.println("title元素:" + lang + " " + text);

                // 获取author元素
                Element author = (Element) book.getElementsByTagName("author").item(0);
                String authorText = author.getFirstChild().getNodeValue();
                System.out.println("author元素:" + authorText);

                // 获取year元素
                Element year = (Element) book.getElementsByTagName("year").item(0);
                String yearText = year.getFirstChild().getNodeValue();
                System.out.println("year元素:" + yearText);

                // 获取price元素
                Element price = (Element) book.getElementsByTagName("price").item(0);
                String priceText = price.getFirstChild().getNodeValue();
                System.out.println("price元素:" + priceText);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果如下图所示:

在这个案例中,我们使用了Java标准库javax.xml.parsers包中的DocumentBuilder和Document类,以及org.w3c.dom包下的Element和NodeList类。

首先,我们会使用DocumentBuilderFactory类创建一个DOM解析器工厂,然后使用newDocumentBuilder()方法创建DOM解析器,并使用parse()方法解析XML文档。

接着,我们又使用getDocumentElement()方法获取到根元素,并使用getElementsByTagName()方法获取所有的book元素。对于每个book元素,我们可以使用getAttribute()和getElementsByTagName()方法来获取元素的属性和子元素,并使用getFirstChild()和getNodeValue()方法来获取属性和文本内容,最后将它们打印到控制台上。

以上这个代码案例其实只是一个简单的DOM解析案例,实际上,DOM可以提供非常丰富的API来访问和操作XML文档。如果你想要处理复杂的XML文档,或者想进行复杂的数据操作,都可以使用DOM解析器。

4. Pull解析

我们在Java中进行Pull解析,需要依赖javax.xml.stream包中的相关类,这些类包括XMLInputFactory、XMLStreamReader等。这些类都是Java标准库中的一部分,因此不需要额外下载其他的依赖包。

java 复制代码
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

/**
 * @author 一一哥Sun
 * @company 千锋教育
 */
public class Demo02 {
    public static void main(String[] args) {
        try {
            // 创建XML解析器工厂
            XMLInputFactory factory = XMLInputFactory.newInstance();
            // 创建XML解析器
            XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("src/books.xml"));
            // 遍历XML文档
            while (reader.hasNext()) {
                //获取下一个事件
                int event = reader.next();
                switch (event) {
                    case XMLStreamConstants.START_ELEMENT://开始事件
                        System.out.print(reader.getLocalName() + ": ");
                        // 打印元素的属性
                        for (int i = 0; i < reader.getAttributeCount(); i++) {
                        	//得到元素中的属性名及属性值
                            System.out.print(reader.getAttributeLocalName(i) + "=" + reader.getAttributeValue(i));
                        }
                        System.out.println();
                    break;
                    case XMLStreamConstants.CHARACTERS://处理字符事件
                        String text = reader.getText().trim();
                        if (!text.isEmpty()) {
                            System.out.println(text);
                        }
                    break;
                }
            }
            // 关闭XML解析器
            reader.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (XMLStreamException e) {
            e.printStackTrace();
        }
    }
}

上面这个代码案例,我们使用了Java标准库javax.xml.stream包中的XMLInputFactory和XMLStreamReader类。

首先,我们使用XMLInputFactory类创建了一个Pull解析器工厂。

然后使用createXMLStreamReader()方法创建了一个Pull解析器,并使用next()方法遍历XML文档。

对于每个事件,我们使用switch语句进行处理。对于START_ELEMENT事件,我们使用getLocalName()方法获取元素名称,并使用getAttributeCount()、getAttributeLocalName()和getAttributeValue()方法获取元素的属性。

对于XMLStreamConstants.CHARACTERS事件,我们使用getText()方法获取文本内容,并使用trim()方法去除空白字符,最后将文本内容打印到控制台上。XMLStreamConstants.CHARACTERS是Pull解析器在读取到XML文档中的字符数据时触发的事件。在这个事件中,我们可以使用XMLStreamReader类的getText()方法来获取字符数据,并使用trim()方法去除空白字符。由于XML文档中的空白字符也会被解析器读取到,因此我们需要使用trim()方法来排除空白字符,只获取有意义的文本内容。

在今天的文章中,壹哥重点给大家讲解DOM解析与Pull解析两种解析方式,其他的两种解析方式,大家如果感兴趣可以自行学习哦。

5. 总结对比

以上几种XML解析方式都有各自的特点,比如:

  • DOM解析给我们提供了非常方便的操作API,但需要将整个XML文档加载到内存中,对于大型XML文件来说,性能和内存占用都会比较高。
  • SAX和StAX不需要将整个XML文档加载到内存中,因此性能和内存占用都会比较低,但编程复杂度较高。
  • Pull解析器的API更加简单和直观,同时不需要将整个XML文档加载到内存中,因此性能和内存占用都会比较低,但功能相对较弱,不适合处理复杂的XML文档。

我们可以通过一个表格来清晰地对比不同解析方式的优缺点:

解析技术 优点 缺点
DOM 提供非常方便的操作API 需要将整个XML文档加载到内存中,对于大型XML文件来说,性能和内存占用都会比较高
SAX 不需要将整个XML文档加载到内存中,性能和内存占用都会比较低 编程复杂度较高,需要注册回调函数来处理事件
StAX API简单和直观,不需要将整个XML文档加载到内存中,性能和内存占用都会比较低 编程复杂度较高,需要使用迭代器来遍历XML文档
Pull API简单和直观,不需要将整个XML文档加载到内存中,性能和内存占用都会比较低 功能相对较弱,不适合处理复杂的XML文档

以上就是壹哥 对DOM、SAX、StAX和Pull这四种常用的XML解析技术的优缺点分析,实际上每种技术都有其优点和缺点,我们在开发时可以根据自己的实际需求选择合适的技术。如果需要处理复杂的XML文档,或者需要进行复杂的数据操作,且内存资源较充足,壹哥建议使用DOM或SAX解析器;如果XML文档相对简单,数据操作较为简单,且内存资源比较宝贵,我们可以考虑使用StAX或Pull解析器。

------------------------------正片已结束,来根事后烟----------------------------

三. 结语

壹哥一直跟大家强调,这个世界上没有十全十美的技术,虽然XML有挺多优点,但它也存在一些缺陷,比如:

  • XML语法比较繁琐,需要使用大量的标记和属性来描述数据,因此相对于其他格式来说会比较冗长。
  • XML解析需要比较大的内存开销,这对于大型XML文件来说是一个问题;
  • XML的可扩展性和自由度也是它的缺陷之一,因为这样会导致XML文档的结构比较复杂,不同的应用程序可能会定义不同的标签和属性,从而导致XML文档之间的互操作性变得很困难,很难实现统一的操作模式。
  • XML文档的解析和处理需要使用专门的工具和库,这对于一些小型应用程序来说可能会增加开发成本。

正是基于以上这些缺陷,导致现在很多的项目中已经不再优先考虑XML文档作为数据存储和传输载体了,现在一般的项目大多都是倾向于使用JSON格式。所以在下一篇文章中,壹哥 会给大家普及JSON及其解析技术,欢迎关注哦。另外如果你独自学习觉得有很多困难,可以加入壹哥的学习互助群,大家一起交流学习。

相关推荐
IT毕设实战小研5 分钟前
基于大数据的跨国外派人员适应满意度与留存影响因素可视化分析
android·java·大数据·python·django·课程设计
letisgo59 分钟前
JAVA 高级进阶10篇《消息队列实战:RocketMQ/Kafka选型与“不丢不重有序“三连解》
java·面试·kafka·消息队列·rocketmq
2401_8504811714 分钟前
UVA101
java
平头哥AI19 分钟前
Day 21 _ error 是个普通值_errors.New 与 fmt.Errorf 造出来,沿调用栈抛到 main 接住
后端·学习·golang·go
Lyyaoo.24 分钟前
【回溯】【中等】全排列
java·数据结构·算法
xixingzhe224 分钟前
spring boot对接Dify
java·spring boot·dify
数据狐(Datafox)26 分钟前
京东商品详情API接口解析(附 JSON 样例)
java·开发语言·json
写后端的胖头鱼28 分钟前
一文讲懂JVM与调优
jvm·后端·算法·架构·jvm调优
步行cgn29 分钟前
Spring Boot 指定数据来源详解
spring boot·后端·python
Wang's Blog30 分钟前
Java框架快速入门: Spring Security+OAuth2之深度定制UserDetailsService与密码无缝升级
java·开发语言·spring