失传的 XML 技艺(TRANSLATE)
注:这是一篇我从marcosmagueta.com/blog中翻译来的文章,可能存在一些模糊不清的表达,见谅。
Title: The lost art of XML
Release time: January 20, 2026
前言
软件工程界对 XML 存在一种奇特的遗忘症。在大多数圈子里提及它,你会收获心领神会的微笑、不屑一顾的摆手,以及那种专门留给被认为过时技术的居高临下的认可。"哦,XML 啊,"他们这样说,仿佛这几个音节本身就承载着过时的重量。"我们现在用 JSON 了。简洁多了。"
这纯属无稽之谈。
XML 并非因能力不足而被弃用,而是因为 JavaScript 胜出了。浏览器胜出了。在这场胜利中,我们集体默认了一个事实:一种为人类在 REPL(Read-Eval-Print Loop 读取-求值-打印循环) 环境中可读性而设计的格式,竟被用于机器间通信、配置管理以及任何需要严谨性的场景。为了工具使用的便利,我们放弃了逻辑形式主义。
XML 的价值所在
思考 XML 实际提供了什么,以及我们在追求极简主义过程中放弃了什么:
模式定义。XML 模式定义(XSD)在文档层面提供了真正的类型检查。你可以规定某个元素必须包含整数、必须恰好出现一次、某些属性是必需的。模式本身也是一个文档;它可以被验证、版本控制、引用。当你收到 XML 文档时,在解析内容之前就能验证其结构。这并非奢侈功能,而是工程规范的基本要求。
JSON 格式本身并未内置此类机制。诚然存在 JSON Schema,但它属于事后补救方案,是第三方附加产物,从未获得普遍采用。大多数 JSON 数据(如果确实经过验证)都是通过临时编写的代码来检查预期键是否存在,然后听天由命。这种将草率伪装成务实的行为堪称荒谬。
命名空间。XML 允许您组合来自多个模式的文档而不会产生冲突。您可以在自定义词汇表中嵌入 XHTML,引用外部定义,在不同语义域之间保持清晰界限。这并非理论空谈,而是 SVG、MathML 和 SOAP 等标准在实际工作中的实现方式。
JSON 对此毫无应对之策。若两个库使用相同的键名,您只能临时凑合:添加前缀、随意嵌套、然后祈祷不出错。
注释功能。XML 将注释作为原生特性支持。您可以为配置添加标注,解释特定数值存在的缘由,为后续维护者留下说明。而 JSON 不支持注释——官方规范明确禁止。我反复听闻的理由是:注释会增加解析复杂度(这说法听来荒谬,但世事难料)。
自描述性。XML 文档自带其模式,或明确引用模式。结构是声明式的。类型是显式的。你可以将 XML 文件交给他人,他们通过合理努力就能理解其含义,无需查阅外部文档。
JSON 是一系列嵌套的字典和数组,键为字符串。 "status": 1 代表什么? "type" 的有效值有哪些? "timestamp"是整数还是字符串?你需要阅读 API 文档。你需要祈祷文档存在且是最新的。
S-表达式的关联
对于熟悉 Lisp 的人来说,XML 的结构会立刻感到亲切。它本质上是使用尖括号而非圆括号的 S-表达式。元素是带标签的列表;属性是元数据;嵌套是组合。这种映射是直接的:
(person (name "Alice") (age 30))<person>
<name>Alice</name>
<age>30</age>
</person>或者使用属性:
<person name="Alice" age="30" />这并非偶然。XML 继承自 SGML,而 SGML 又继承了更早的标记传统,但其核心理念(即数据可以表示为嵌套的、带标签的结构)与驱动 Lisp 强大能力的理念如出一辙。代码即数据。结构即意义。
相比之下,JSON 是 JavaScript 中的对象字面量。它是一种初始化字典的表示法。它从未被设计为数据交换格式;之所以被推上这个位置,是因为它已经存在于浏览器中,且开发者已经熟悉它。便利性优先于正确性。
我们为何做出了糟糕的选择
放弃 XML 而转向 JSON 和其他简化格式(如 YAML)是一个典型案例,展示了开发者体验如何压倒技术优势。XML 冗长,需要闭合标签,与 JSON 的极简主义相比显得“笨重”。这些以工程关切为名的抱怨,本质上是审美偏好。
冗长虽常被视为缺陷,但当它服务于清晰性时,便不是缺点。两者并不互斥。闭合标签使结构明确,消除了解析时的歧义。尖括号的存在并非为了惹恼你,而是为了区分标记与内容,让文档结构一目了然。
另一个冒牌货 YAML,既模糊又脆弱。数据格式采用对缩进敏感的语法?隐式类型猜测 no 是布尔值还是字符串?规范如此复杂,以至于实现在边缘案例上存在分歧?这难道不是我们本应抛弃的上下文依赖解析吗?
论开发者便利与自我欺骗
业界拒绝承认一个区别:开发者的便利性与正确性是不同的考量。它们不一定对立,但绝非同一回事。一种格式可能输入不便,却仍是正确选择;一种格式可能使用愉快,却仍存在根本性缺陷。
我们投入了数十亿美元和无数工程时间,只为让糟糕的技术变得更快。JVM 或许是最典型的例子;这个最初为遥控器设计的虚拟机,通过纯粹的优化力量、数百万开发者的工时以及央行印出的钞票,最终成为了企业软件的基石。数十年的工作投入到即时编译、垃圾回收算法、逃逸分析中,全都是为了让一个本质上笨拙的平台达到可接受的性能。而它成功了!JVM 现在确实很快。
但想象一下,如果当初能将其中一小部分精力投入到更优的方案上。想象一下,如果我们选择了一个专为解决实际问题而设计的平台,而不是将一个玩具强行改造用于生产环境。我们花费了数十亿让错误的选择勉强运作,而本可以仅用数百万就让正确的选择带来愉悦体验。
这正是 JSON 的发展模式。我们选择它,是因为它方便,因为它已内置于浏览器,因为开发者早已熟悉对象字面量。然而,当它的局限性逐渐显现时,我们却耗费巨大力气去规避这些缺陷:创建验证库、发明类型系统(如 TypeScript)、为 API 客户端构建代码生成器、开发整套框架来管理无类型数据结构带来的混乱。
我们本可以直接使用 XML。它早已具备模式验证功能,类型系统一应俱全,工具链也相当成熟。但 XML 看起来不够美观,闭合标签显得冗长,于是我们选择了 JSON,随后却耗费数年时间重建 XML 早已提供的功能。
这种合理化过程令人瞩目。他们说"JSON 更简单",却维护着数千行验证代码;他们声称"JSON 可读性更强",却在调试因键名拼写错误引发的隐蔽漏洞——而这些错误本可通过模式定义立即捕获;他们坚称"JSON 更轻量",却在传输数兆字节的冗余字段名——这些冗余数据若采用二进制 XML 格式早被压缩消除。
这不是工程实践,这是伪装成技术判断的时尚风潮。
物理形态与概念模型
这里还存在另一种混淆:我们放任物理表现形式主导了概念模型。XML 的尖括号只是序列化方案的选择,并非 XML 的本质核心。真正的核心在于信息集合——即元素、属性和内容的抽象模型。至于这个模型采用何种物理编码方式(带尖括号的文本格式、Fast Infoset 二进制格式或 EXI 压缩格式),完全是另一个层面的考量。
但由于文本序列化看起来“笨重”,我们便否定了整个模型。我们抛弃了模式、命名空间、验证、自描述性,这一切仅仅因为我们不喜欢尖括号。这就像因为不喜欢 SQL 而拒绝关系型数据库一样。SQL 并非关系模型。说实话,大多数对 XML 的厌恶源于多年来人们不得不应对的协议和晦涩工具:一位朋友曾向我讲述他与尼日利亚中央银行进行集成时的恐怖经历,他们显然使用了 SOAP;但这些都是实现层面的问题。虽然分析我们对某个模型的投入程度是个好主意,但同样重要的是要认识到,形式主义本身不应为尼日利亚中央银行如何应用它而承担责任。
JSON 将这几个层面混为一谈。它既是数据模型(嵌套对象和数组),又是序列化格式(带字符串键的大括号和方括号)。不存在独立于文本表示的抽象模型。这意味着你无法拥有保留相同语义的二进制 JSON,因为语义即语法。所有类似 JSON 的二进制格式(MessagePack、BSON 等)都是具有不同权衡取舍的独立模型。
XML 恰当地分离了这些关注点。信息集是模型。文本、二进制、压缩二进制都只是该模型的序列化形式。你可以根据约束条件选择合适的序列化方式,而不会改变数据的含义。这才是正确的分层设计。系统本应如此构建。
但当你编写快速 API 端点时,正确的分层设计并不方便,于是我们选择了这种混乱的混合方案,并称之为进步。
我们失去了什么
当我们抛弃 XML 时,我们失去了:
- First-class validation through schemas
通过模式实现的一流验证 - Namespacing and composition
命名空间和组合 - Comments and self-documentation
注释和自我文档化 - A separation between structure and content
结构与内容的分离 - Tooling that could verify correctness before runtime
能够在运行前验证正确性的工具 - A format that could evolve without breaking existing consumers
无需破坏现有消费者即可演进的格式
What we gained: 我们获得的优势:
- Fewer characters 字符更少
- Easier hand-writing for trivial cases
简单场景下手写更轻松 - Native parsing in JavaScript
JavaScript 原生解析支持
Fantastic trade. 绝佳的交易。
二进制答案
关于 XML 的常见抱怨之一是其冗长性,特别是在网络传输方面。"所有那些闭合标签浪费带宽"。但即使表面接受这种担忧(我们本不该如此),XML 多年前就已解决了这个问题。
快速信息集(Fast Infoset)作为 ITU-T Rec. X.891 和 ISO/IEC 24824-1 标准,是 XML 信息集的二进制编码。它提供相同的逻辑结构、相同的模式验证、相同的语义丰富性,但显著减少了数据大小和解析开销。XML 文档可序列化为快速信息集进行传输,然后在接收端反序列化回标准 XML。模式保持不变。工具链保持不变。仅传输格式发生变化。
EXI(高效 XML 交换)作为另一项 W3C 标准更进一步。它采用基于模式的压缩技术,实现与手工调优二进制格式相媲美的数据大小,同时保持 XML 的语义模型。您既能获得类型安全、验证和自描述特性,又无需承受所谓的带宽代价。
这些格式是存在的。它们已被标准化。它们拥有多种语言的实现。然而,整个行业却集体忽视它们,转而青睐 JSON,然后又发明 Protocol Buffers 和其他二进制格式来解决那些二进制 XML 早已解决的问题。
这种模式颇具启发性:我们因 XML 冗长而弃用,当处理 JSON 变得过于痛苦时,我们创建了缺乏 XML 语义丰富性的新二进制格式,却带着一种困惑的认知——“我们需要某种东西,但那到底是什么!?”。我们本可以直接使用那些已经存在的二进制 XML 编码。但这将需要承认 XML 一直以来都是正确的。
关于实用性
我并非主张 XML 应被用于所有地方。在某些情况下,其他格式更为合适:协作服务间的小型数据传输,以及模式验证会显得小题大做的场景。但这些是例外,而非规则。
对于任何需要持久性的内容,对于任何将被多个系统使用的内容,对于任何正确性比便利性更重要的内容;XML 仍然是更优的选择。我们集体假装并非如此的事实,证明了我们自欺欺人的能力。
我们选择了更容易输入的格式,而不是更难误用的格式。我们选择了开发者的便利,而不是系统的可靠性。而现在,当我们的 JSON API 偏离轨道,当我们的配置悄无声息地崩溃,当我们的数据缺乏我们以为它应有的结构时,我们却表现得惊讶。
最后一点
微软,尽管有种种缺点,却理解这一点。MSBuild 使用 XML。WPF 使用 XML。.NET 的配置系统在.NET Core 中屈服于 JSON 压力之前一直使用 XML。这些并非随意的选择;它们认识到复杂系统需要复杂的表示形式,而数据表示的形式化可以防止整类错误。
我们现在认为 XML"过时"这一事实,更多地反映了我们当前的优先取向,而非 XML 的实际效用。我们更看重击键的经济性而非语义的精确性,更青睐熟悉感而非严谨性,更追求表面上的简洁而非真正的简洁——那种源于清晰规则与一致结构的本质简洁。
XML 并非完美无瑕。XPath 繁复冗余,XSLT 堪称炼狱,各类企业委员会催生的 XML"标准"实属过度设计的纪念碑,相关库函数往往糟糕透顶,安全漏洞更是层出不穷。但核心格式(元素、属性、模式、命名空间)依然坚实可靠,本文仅旨在阐明这一点。我们不应因其被滥用而全盘否定其机制本身。
我厌倦了像 JSON 这样被阉割的格式被奉为默认选项、"现代"选择乃至理所当然的正确方案。它们绝非如此。这些不过是路径依赖与时尚潮流的产物,而非深思熟虑的工程判断。
Sometimes the old way was the right way. This is one of those times.
有时旧方法才是正确之道。此刻正是如此。
评论已关闭