XML全称是eXtensible Markup Language,中文叫做可扩展标记语言。它的设计初衷是为了传输和存储数据,而不是展示数据。与HTML专注于页面的呈现不同,XML关注的是数据本身的内容和结构,它允许开发者自定义标签名称,用来描述各种各样真实世界中的信息,比如一本书的标题、作者和价格,或者一个用户的姓名、邮箱和电话。正因为这种灵活性和自描述性,XML在配置文件、接口报文、文档交换等场景中被广泛使用,即使如今JSON盛行,XML依然是很多企业级系统和传统协议中不可替代的角色。

XML的基本结构与语法规则
一份规范的XML文档通常以声明开头,声明用于说明XML的版本以及文档所使用的字符编码。声明必须写在文档的第一行,前面不能有任何空格或空行。下面是一个非常典型的XML示例,描述了一份简单的图书信息:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="计算机">
<title>深入理解计算机系统</title>
<author>Randal E. Bryant</author>
<price>139.00</price>
</book>
<book category="文学">
<title>活着</title>
<author>余华</author>
<price>28.00</price>
</book>
</bookstore>从上面的例子可以看出,XML文档是一棵严格的树形结构。整个文档必须有且只有一个根元素,这里就是bookstore元素,其余所有元素都是它的子元素,层层嵌套下去。每个元素由开始标签和结束标签组成,标签名区分大小写,比如<Book>和<book>会被视为两个完全不同的标签,这一点和HTML的宽松处理方式截然不同。
XML对格式的检查非常严格,有几条规则必须牢记:一是所有标签必须正确闭合,空元素可以使用自闭合写法,例如<br />;二是标签不能交叉嵌套,必须先开的后关,像<a><b></a></b>这样的写法是非法的;三是属性值必须用引号包裹,单引号或双引号均可,但不能省略。满足这些规则的XML被称为格式良好的XML,如果格式不正确,绝大多数解析器会直接报错拒绝处理,这其实是XML严谨性带来的好处,能够在数据交换的源头就发现错误。
除了元素和属性,XML还支持注释、CDATA段和特殊字符转义。注释的写法与HTML相同,使用<!-- 注释内容 -->的形式,注释不能出现在声明之前,也不能嵌套。当需要写入大量包含尖括号等特殊字符的文本时,比如一段代码片段,可以使用CDATA段包裹,解析器会将其中的内容当作纯文本处理:
<description>
<![CDATA[
if (a < b && b < c) { return true; }
]]>
</description>XML与HTML的核心区别
初学者最容易混淆的就是XML和HTML的关系。两者都源自SGML,都使用标签和属性来组织内容,外形上非常相似,但设计目标完全不同。HTML中的标签是预先定义好的,比如<div>、<p>、<img>,它们各自有浏览器约定俗成的显示效果,写错了标签浏览器往往也能容错渲染。而XML中的标签完全由开发者自定义,没有任何预设的显示含义,它的价值在于描述数据的结构和含义。
可以从几个维度来对比。第一,目的不同:HTML用来展示数据,XML用来描述和传输数据。第二,语法严格程度不同:HTML允许省略某些闭合标签、属性值可以不加引号,XML则要求所有标签严格闭合、大小写一致、属性必须带引号。第三,标签是否可扩展:HTML的标签集是固定的,XML的标签由开发者根据业务自由定义。下面的表格做一个直观的总结:
| 对比项 | XML | HTML | |||
|---|---|---|---|---|---|
| 设计目的 | 存储和传输数据 | 展示数据 | 标签是否自定义 | 完全自定义 | 预定义标签 |
| 语法要求 | 严格,格式错误则解析失败 | 宽松,浏览器自动容错 | |||
| 大小写敏感 | 敏感 | 不敏感 |
理解了这个区别之后,就能明白为什么很多系统之间的数据交换选择XML作为载体。因为XML自带结构描述能力,接收方即使事先不了解数据内容,也能通过标签名称理解每个字段的含义,这种自描述性在异构系统集成时非常宝贵。当然,也正因为标签冗余较多,同样一份 XML 文档的体积通常比等价的JSON大不少,这也是后来JSON在Web领域逐渐占优的原因之一。
XML的实际应用场景与解析方式
XML在实际开发中的应用非常广泛。最常见的就是各类配置文件,比如早期Java开发中的web.xml、Spring的applicationContext.xml、Maven的pom.xml,以及Android开发中的布局文件和清单文件,都采用XML格式。此外,很多Web Service接口、RSS订阅源、Office文档格式(docx本质上就是一个XML文档的压缩包)也都基于XML构建。可以说,即便你不主动使用XML,它也广泛存在于你每天接触的软件之中。
有了XML文档,程序就需要读取其中的数据,这个过程叫做解析。主流的解析方式有两种:DOM解析和SAX解析。DOM解析会把整个XML文档一次性加载到内存中,构建成一棵树形对象结构,之后可以随机访问任意节点,修改、删除、遍历都非常方便。它的缺点是内存占用大,不适合处理特别大的文档。以Python为例,使用内置的xml.dom.minidom模块解析上面的图书XML:
from xml.dom.minidom import parse
# 解析XML文档并构建DOM树
dom = parse("books.xml")
books = dom.getElementsByTagName("book")
for book in books:
title = book.getElementsByTagName("title")[0].firstChild.data
price = book.getElementsByTagName("price")[0].firstChild.data
print("书名:%s,价格:%s" % (title, price))SAX解析则采用事件驱动的流式方式,解析器从头到尾顺序读取文档,遇到开始标签、结束标签、文本内容时会依次触发相应的回调函数,程序在这些回调中处理数据。它不需要把整个文档载入内存,因此非常适合处理体量巨大的XML文件,缺点是只能顺序读取、无法回退,也不方便直接修改文档结构。除了这两种传统方式,很多语言还提供了折中的方案,例如Java中的JDOM和DOM4J、Python中更友好的ElementTree,它们在易用性和性能之间取得了较好的平衡:
import xml.etree.ElementTree as ET
tree = ET.parse("books.xml")
root = tree.getroot()
# 遍历所有book节点,读取子元素内容
for book in root.findall("book"):
print(book.get("category"),
book.find("title").text,
book.find("price").text)总的来说,学习XML不需要死记硬背太多概念,重点掌握三点即可:一是树形结构和标签嵌套规则,保证写出的文档格式良好;二是元素与属性的选择,一般来说,描述数据内容用元素文本,描述元信息用属性,一个节点上同一个信息不要既写成属性又写成子元素;三是根据实际数据量选择合适的解析方式,小文件用DOM图省事,大文件用SAX或StAX控制内存。掌握了这些基础,再去接触DTD和XML Schema等校验机制、XPath查询语言、XSLT转换等进阶内容时,就会顺利很多。XML虽然看起来有些老派,但它背后的树形数据建模思想是通用的,值得每个开发者认真学一遍。