💻 第十一章 XML究竟是什么
📄 一句话概括

XML 文本要变成程序里的数据得靠解析器——DOM 把整棵树读进内存慢而全,SAX 边读边报事件省内存,两种流派各挑场景。

📝 本节和前面几节的关系

规矩立完,本节看工程落地的最后一环:程序究竟怎么把 XML 读进内存。

一、解析器干什么

把 XML 文本解析成程序能用的结构:元素、属性、文本——术语叫解析器(parser); 各语言标准库通常自带:Python 的 xml.etree、Java 的 JAXP、C# 的 System.Xml; 选择解析方式的前提是文档有多大、要读几遍——这是 DOM 与 SAX 分家的原因。

二、DOM 型:整棵树搬进内存

一次把整份文档读成元素树,之后随便遍历、查找、修改; 优点是齐全、可随机访问;缺点是吃内存——文档多大,内存占用同量级; 适合中小文档、需要反复访问或改写的场景(本章 Demo 用的就是这类接口)。

三、SAX 型:边读边报事件

不建树,从头扫到尾,遇到元素开始/结束就报一个事件给程序; 优点是省内存(只保存当前状态),适合几百 MB 的大文件; 缺点是只能一次过、不能回看——要重读得重扫,且程序得自己维护上下文; 【⚠️ 2026 现状】各语言另有折中流派(如 ElementTree 的 iterparse、流式 JSON 解析器),大文件【边下边解】已是通用工程手法。

四、动手 Demo:两种读法读同一份文档

先用树式接口遍历取出姓名,再用事件式接口把【开始/结束】事件逐个打出来。

import io
import xml.etree.ElementTree as ET
doc = "<班级><学生><姓名>张三</姓名></学生><学生><姓名>李四</姓名></学生></班级>"
tree = ET.fromstring(doc)
print([e.find("姓名").text for e in tree.findall("学生")])
for ev, el in ET.iterparse(io.StringIO("<班级><学生/><学生/></班级>"),
                           events=("start", "end")):
    print(ev, el.tag)

实跑输出:

--------------------------------------------------------------------
用于解析 XML 的组件
--------------------------------------------------------------------

XML 文档要变成程序里的数据, 得靠解析器。两大流派:

  流派       做法                         适合
  ---------------------------------------------------------------
  DOM 型     整棵树读进内存, 慢慢遍历     要反复改查的小中型文档
  SAX 型     边读边报事件, 不建树         超大文档、只扫一遍

    [DOM 派] 把文档整棵读进来, 数出所有元素:
        遍历得到: ['张三', '李四']

    [SAX 派] 不建树, 事件流【开始/结束】逐个报:
        start -> 班级
        start -> 学生
        end   -> 学生
        start -> 学生
        end   -> 学生
        end   -> 班级

    同一份文档两种读法: DOM 齐全但吃内存; SAX 省内存但只能一次过——
    各语言的标准库都自带这两种, 本章 Demo 用的是 Python 标准库 xml.etree。

树式输出两个姓名;事件式输出 start 班级、start 学生、end 学生……共 7 个事件。 注意事件式那份文档里根本没有姓名文本——SAX 关心的只是【结构事件流】。

30 秒实验

  1. 跑一遍两种解析方式,说出各自的内存特点适合什么规模的文档。
  2. 找一个你电脑上的大 XML/SVG 文件,想一想该选哪种方式读它。

常见误区

  1. 以为解析方式只有一种——DOM 与 SAX 各有地盘,选错会很吃力。
  2. 以为 SAX 更先进就该一律用它——不能回看、要自己维护上下文,小文档用它反而更麻烦。
自测三问(点开对答案) 1. DOM 与 SAX 的核心区别? —— DOM 一次建整棵树(全但吃内存),SAX 边读边报事件(省内存但只能一次过)。 2. 什么时候选 SAX? —— 文档超大、只需扫一遍时。 3. 解析器在各语言里的形态? —— 通常是标准库自带组件,如 Python 的 xml.etree、Java 的 JAXP。

相关笔记