从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • RSS
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金等
  • 职场规划
  • 关于离职
  • 杂谈
  • 📖 读书

    • 读书工具
    • 读书笔记
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • RSS
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金等
  • 职场规划
  • 关于离职
  • 杂谈
  • 📖 读书

    • 读书工具
    • 读书笔记
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

  • JavaSenior

  • JavaEE

    • JavaEE概念

    • Ant

    • Maven

    • 日志

    • Junit

    • JDBC

    • XML和JSON

      • 什么是 XML
      • 解析 XML 的方式
        • 使用 DOM
        • 使用 SAX
      • 使用 Jackson 解析 XML
      • JSON
      • 小结
  • JavaWeb

  • Spring

  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java
  • JavaEE
  • XML和JSON
2023-04-17
目录

解析 XML 的方式

# 01.解析 XML 的方式

XML 是一种树形结构的文档,它有两种标准的解析 API:

  • DOM:一次性读取 XML,并在内存中表示为树形结构;
  • SAX:以流的形式读取 XML,使用事件回调。

# 使用 DOM

DOM 是 Document Object Model 的缩写,DOM 模型就是把 XML 结构作为一个树形结构处理,从根节点开始,每个节点都可以包含任意个子节点。

我们以下面的 XML 为例:

<?xml version="1.0" encoding="UTF-8" ?>
<book id="1">
    <name>Java核心技术</name>
    <author>Cay S. Horstmann</author>
    <isbn lang="CN">1234567</isbn>
    <tags>
        <tag>Java</tag>
        <tag>Network</tag>
    </tags>
    <pubDate/>
</book>
1
2
3
4
5
6
7
8
9
10
11

如果解析为 DOM 结构,它大概长这样:

                      ┌─────────┐
                      │document │
                      └─────────┘
                           │
                           ▼
                      ┌─────────┐
                      │  book   │
                      └─────────┘
                           │
     ┌──────────┬──────────┼──────────┬──────────┐
     ▼          ▼          ▼          ▼          ▼
┌─────────┐┌─────────┐┌─────────┐┌─────────┐┌─────────┐
│  name   ││ author  ││  isbn   ││  tags   ││ pubDate │
└─────────┘└─────────┘└─────────┘└─────────┘└─────────┘
                                      │
                                 ┌────┴────┐
                                 ▼         ▼
                             ┌───────┐ ┌───────┐
                             │  tag  │ │  tag  │
                             └───────┘ └───────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

注意到最顶层的 document 代表 XML 文档,它是真正的“根”,而 <book> 虽然是根元素,但它是 document 的一个子节点。

Java 提供了 DOM API 来解析 XML,它使用下面的对象来表示 XML 的内容:

  • Document:代表整个 XML 文档;
  • Element:代表一个 XML 元素;
  • Attribute:代表一个元素的某个属性。

使用 DOM API 解析一个 XML 文档的代码如下:

InputStream input = Main.class.getResourceAsStream("/book.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(input);
1
2
3
4

DocumentBuilder.parse() 用于解析一个 XML,它可以接收 InputStream,File 或者 URL,如果解析无误,我们将获得一个 Document 对象,这个对象代表了整个 XML 文档的树形结构,需要遍历以便读取指定元素的值:

static void printNode(Node n, int indent) {
    for (int i = 0; i < indent; i++) {
        System.out.print(' ');
    }
    switch (n.getNodeType()) {
    case Node.DOCUMENT_NODE: // Document 节点
        System.out.println("Document: " + n.getNodeName());
        break;
    case Node.ELEMENT_NODE: // 元素节点
        System.out.println("Element: " + n.getNodeName());
        break;
    case Node.TEXT_NODE: // 文本
        System.out.println("Text: " + n.getNodeName() + " = " + n.getNodeValue());
        break;
    case Node.ATTRIBUTE_NODE: // 属性
        System.out.println("Attr: " + n.getNodeName() + " = " + n.getNodeValue());
        break;
    default: // 其他
        System.out.println("NodeType: " + n.getNodeType() + ", NodeName: " + n.getNodeName());
    }
    for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) {
        printNode(child, indent + 1);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

解析结构如下:

Document: #document
 Element: book
  Text: #text = 
  
  Element: name
   Text: #text = Java核心技术
  Text: #text = 
  
  Element: author
   Text: #text = Cay S. Horstmann
  Text: #text = 
  ...
1
2
3
4
5
6
7
8
9
10
11
12

对于 DOM API 解析出来的结构,我们从根节点 Document 出发,可以遍历所有子节点,获取所有元素、属性、文本数据,还可以包括注释,这些节点被统称为 Node,每个 Node 都有自己的 Type,根据 Type 来区分一个 Node 到底是元素,还是属性,还是文本,等等。

使用 DOM API 时,如果要读取某个元素的文本,需要访问它的 Text 类型的子节点,所以使用起来还是比较繁琐的。

# 使用 SAX

使用 DOM 解析 XML 的优点是用起来省事,但它的主要缺点是内存占用太大。

另一种解析 XML 的方式是 SAX。SAX 是 Simple API for XML 的缩写,它是一种基于流的解析方式,边读取 XML 边解析,并以事件回调的方式让调用者获取数据。因为是一边读一边解析,所以无论 XML 有多大,占用的内存都很小。

SAX 解析会触发一系列事件:

  • startDocument:开始读取 XML 文档;
  • startElement:读取到了一个元素,例如 <book>;
  • characters:读取到了字符;
  • endElement:读取到了一个结束的元素,例如 </book>;
  • endDocument:读取 XML 文档结束。

如果我们用 SAX API 解析 XML,Java 代码如下:

InputStream input = Main.class.getResourceAsStream("/book.xml");
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser saxParser = spf.newSAXParser();
saxParser.parse(input, new MyHandler());
1
2
3
4

关键代码 SAXParser.parse() 除了需要传入一个 InputStream 外,还需要传入一个回调对象,这个对象要继承自 DefaultHandler:

class MyHandler extends DefaultHandler {
    public void startDocument() throws SAXException {
        print("start document");
    }

    public void endDocument() throws SAXException {
        print("end document");
    }

    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        print("start element:", localName, qName);
    }

    public void endElement(String uri, String localName, String qName) throws SAXException {
        print("end element:", localName, qName);
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        print("characters:", new String(ch, start, length));
    }

    public void error(SAXParseException e) throws SAXException {
        print("error:", e);
    }

    void print(Object... objs) {
        for (Object obj : objs) {
            System.out.print(obj);
            System.out.print(" ");
        }
        System.out.println();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

运行 SAX 解析代码,可以打印出下面的结果:

start document
start element:  book
characters:
   
start element:  name
characters: Java核心技术
end element:  name
characters:
   
start element:  author
...
1
2
3
4
5
6
7
8
9
10
11

如果要读取 <name> 节点的文本,我们就必须在解析过程中根据 startElement() 和 endElement() 定位当前正在读取的节点,可以使用栈结构保存,每遇到一个 startElement() 入栈,每遇到一个 endElement() 出栈,这样,读到 characters() 时我们才知道当前读取的文本是哪个节点的。可见,使用 SAX API 仍然比较麻烦。 ‍

上次更新: 2025/5/5 17:15:09
什么是 XML
使用 Jackson 解析 XML

← 什么是 XML 使用 Jackson 解析 XML→

最近更新
01
2025 年 4 月记
04-30
02
山西大同 “订婚强奸案” 将会给整个社会带来的影响有多严重? - 知乎 转载
04-26
03
一个小技巧,让电子书阅读体验翻倍!
04-18
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式