Skip to content

XML 速查表

用于结构化数据交换的可扩展标记语言。

01

入门

XML 文档结构

XML 文档以 XML 声明开头。元素必须正确嵌套和闭合。属性提供额外元数据。xmlns 声明 XML 命名空间以避免命名冲突。

xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- This is a comment -->
<bookstore xmlns="https://example.com/books">
  <book id="b1" category="fiction">
    <title lang="en">The Great Gatsby</title>
    <author>F. Scott Fitzgerald</author>
    <price currency="USD">12.99</price>
  </book>
  <book id="b2" category="programming">
    <title lang="en">XML Guide</title>
    <author>John Doe</author>
    <price currency="USD">29.99</price>
  </book>
</bookstore>

XML 声明

XML 声明是序言的第一行。'version' 必填(1.0 或 1.1)。'encoding' 默认 UTF-8。'standalone' 告诉解析器是否需要外部 DTD 声明。

xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<!-- version is required; encoding and standalone are optional -->
<!-- encoding defaults to UTF-8 -->
<!-- standalone="yes" means no external markup declarations -->

<root>
  <child>text</child>
</root>

注释与处理指令

注释用 <!-- -->,不能嵌套,内部不能含 '--'。处理指令(PI)如 xml-stylesheet 向解析器传递应用特定信息。PI 用 <?target data?>。

xml
<?xml version="1.0"?>
<!-- a comment: ignored by the parser, cannot contain -- -->

<?xml-stylesheet type="text/xsl" href="style.xsl"?>

<root>
  <!-- comments can span
       multiple lines -->
  <item>data</item>
</root>

良构 XML

良构(well-formed)指:单一根元素、正确嵌套、所有标签闭合、属性加引号、无重复属性。文档必须良构才能被解析;有效性(对照 DTD/Schema)是可选的。

xml
<!-- WELL-FORMED: single root, properly nested, closed -->
<root>
  <a>
    <b>text</b>
  </a>
</root>

<!-- NOT well-formed: two roots, overlapping tags -->
<!-- <a><b></a></b> -->
<!-- <x></x><y></y>   (no single root) -->

<!-- All attributes must be quoted -->
<item id='1' type="x"/>

XML 序言与空白

序言包含 XML 声明和可选的 DOCTYPE。xml:space='preserve' 告诉解析器保留空白;'default' 允许常规处理。元素之间的空白通常无意义。

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root SYSTEM "root.dtd">
<root xml:space="preserve">
  <item>  spaced  text  </item>
  <item xml:space="default">  trimmed  </item>
</root>

CDATA 概览

CDATA 段标记解析器不应解释为标记的文本。CDATA 内 <、>、& 无需转义。CDATA 不能嵌套,且不能包含字面量 ']]>'。

xml
<?xml version="1.0"?>
<root>
  <code><![CDATA[
    if (a < b && c > d) {
      console.log("no escaping needed: < > & ");
    }
  ]]></code>
</root>
02

元素与属性

元素与属性的取舍

属性适合元数据(id、type、标志);元素适合有结构或重复值的数据。一个属性在每个元素只出现一次,且不能承载嵌套结构。无严格规则——一致性最重要。

xml
<!-- Attributes: metadata/identifiers; Elements: data -->
<person id="p1">
  <name>Alice</name>
  <age>30</age>
</person>

<!-- vs. putting everything in attributes -->
<person id="p1" name="Alice" age="30"/>

<!-- Rule of thumb: use elements for data you display,
     attributes for IDs, types, and metadata -->

空元素

空元素没有内容。自闭合形式 <tag/> 是 <tag></tag> 的简写。'/>' 前的空格是为可读性和 XHTML 兼容性的风格约定。

xml
<!-- Three equivalent empty-element forms -->
<br></br>
<br/>
<br />

<!-- empty element with attributes -->
<img src="logo.png" alt="Logo" width="200" height="100"/>

<!-- self-closing with namespace -->
<xlink:link xmlns:xlink="http://www.w3.org/1999/xlink" href="#sec1"/>

嵌套元素

元素按层级嵌套,必须以相反顺序闭合——不能重叠。嵌套表达结构。过深的树虽合法,但会影响可读性和解析性能。

xml
<library>
  <section name="fiction">
    <book>
      <title>Book A</title>
      <authors>
        <author>Author 1</author>
        <author>Author 2</author>
      </authors>
    </book>
  </section>
</library>

<!-- WRONG: tags must not overlap -->
<!-- <a><b></a></b> -->

属性值与引号

属性值必须用单引号或双引号括起。双引号内的双引号必须转义为 &quot;。一个属性在每个元素最多出现一次——重复值请用子元素。

xml
<!-- Both single and double quotes are allowed -->
<item id="q1" name='quick'/>
<msg text="He said &quot;hi&quot;"/>
<msg text='She said &apos;hi&apos;'/>

<!-- attribute values MUST be quoted -->
<!-- <item id=q1/>  is NOT well-formed -->

<!-- an attribute can appear at most once per element -->
<point x="1" y="2" z="3"/>

默认与固定属性

DTD 属性声明支持 #REQUIRED(必须出现)、#IMPLIED(可选)、#FIXED(常量值)和字面量默认值。#FIXED 属性必须始终等于其声明值,否则文档无效。

xml
<!-- In a DTD, attributes can have defaults -->
<!ATTLIST item
    type    CDATA     "standard"
    status  CDATA     #REQUIRED
    version CDATA     #FIXED "1.0"
    note    CDATA     #IMPLIED>

<item status="active"/>
<!-- 'type' defaults to "standard", 'version' is fixed,
     'note' is optional (#IMPLIED) -->

元素命名规则

元素名必须以字母或下划线开头,可含字母、数字、连字符、下划线和点。名字不能以 'xml'(任何大小写)开头。XML 大小写敏感,<Tag> 与 <tag> 不同。

xml
<!-- Names: start with a letter or underscore,
     continue with letters, digits, hyphens, underscores, dots -->
<my-element>ok</my-element>
<my.element>ok</my.element>
<my_element>ok</my_element>
<_private>ok</_private>
<x-1>ok</x-1>

<!-- NOT allowed: starting with a digit or 'xml', containing spaces -->
<!-- <2way/> -->
<!-- <my element/> -->
<!-- <XmlTag/>  (names are case-sensitive; 'xml' prefix is reserved) -->
03

命名空间

默认命名空间

默认命名空间(xmlns=URI)作用于声明它的元素及所有无前缀的后代。子元素继承它,除非被覆盖。属性从不使用默认命名空间。

xml
<?xml version="1.0"?>
<table xmlns="http://www.w3.org/1999/xhtml">
  <tr>
    <td>Cell content</td>
    <td>More content</td>
  </tr>
</table>
<!-- All elements without a prefix use the default namespace -->

命名空间前缀

命名空间前缀用 xmlns:prefix=URI 声明,以 prefix:element 形式使用。前缀只是别名——只有 URI 决定身份。前缀大小写敏感。

xml
<?xml version="1.0"?>
<root xmlns:bk="https://example.com/books"
      xmlns:au="https://example.com/authors">
  <bk:book bk:id="b1">
    <au:author au:name="Jane Doe"/>
    <bk:title>XML Guide</bk:title>
  </bk:book>
</root>

多命名空间

单个文档可混合多个命名空间。每个前缀声明一次(通常在根元素上)并复用。在元素上声明命名空间会作用于该元素及其后代。

xml
<?xml version="1.0"?>
<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <m:GetPrice xsi:type="xsd:string"
                xmlns:m="https://example.com/pricing">
      <m:Item>Apples</m:Item>
    </m:GetPrice>
  </soap:Body>
</soap:Envelope>

命名空间作用域

命名空间声明的作用域限于声明它的元素及其后代。子元素内的重新声明会覆盖父元素的前缀映射(仅对该子树)。子元素闭合后,原映射恢复。

xml
<?xml version="1.0"?>
<root xmlns:a="http://a.example">
  <a:item>A</a:item>
  <child xmlns:a="http://b.example">
    <!-- here 'a' is REBOUND to the b.example URI -->
    <a:item>B</a:item>
  </child>
  <!-- outside child, 'a' is back to a.example -->
  <a:item>A again</a:item>
</root>

常见命名空间

这些 URI 是众所周知的标识符,不是必须获取的 URL。xsi 命名空间提供 schema-instance 属性(type、nil、schemaLocation)。XSD、XSL、SVG、SOAP、Atom 和 Dublin Core(dc)各有标准 URI。

xml
<!-- Frequently used namespace URIs -->
xmlns:xsi  = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd  = "http://www.w3.org/2001/XMLSchema"
xmlns:xsl  = "http://www.w3.org/1999/XSL/Transform"
xmlns:svg  = "http://www.w3.org/2000/svg"
xmlns:html = "http://www.w3.org/1999/xhtml"
xmlns:soap = "http://schemas.xmlsoap.org/soap/envelope/"
xmlns:atom = "http://www.w3.org/2005/Atom"
xmlns:dc   = "http://purl.org/dc/elements/1.1/"

XSD 中的目标命名空间

schema 的 targetNamespace 是它所定义元素的命名空间。elementFormDefault='qualified' 表示局部声明的元素属于目标命名空间。tns 前缀是 'this namespace' 的约定。

xml
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="https://example.com/orders"
           xmlns:tns="https://example.com/orders"
           elementFormDefault="qualified">
  <xs:element name="order" type="tns:OrderType"/>
  <xs:complexType name="OrderType">
    <xs:sequence>
      <xs:element name="item" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>
04

CDATA 与实体

预定义实体

XML 预定义五个实体:&lt;(<)、&gt;(>)、&amp;(&)、&apos;(')、&quot;(")。& 必须始终转义(即使在非 CDATA 文本中);< 除作为标签起始外必须转义;引号主要在属性中需注意。

xml
<?xml version="1.0"?>
<root>
  <lt>less than: &lt;</lt>
  <gt>greater than: &gt;</gt>
  <amp>ampersand: &amp;</amp>
  <apos>apostrophe: &apos;</apos>
  <quot>quote: &quot;</quot>
  <!-- &lt; &gt; &amp; &apos; &quot; are the 5 built-in entities -->
</root>

字符引用

字符引用使用 Unicode 码点:&#169;(十进制)或 &#xA9;(十六进制)表示 ©。它们在元素文本和属性值中都有效,不受编码影响。可表示任意 Unicode 字符。

xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <decimal>&#169; 2024 Company</decimal>
  <hex>&#xA9; 2024 Company</hex>
  <euro>Price: &#x20AC;10</euro>
  <emoji>Smile: &#x1F600;</emoji>
</root>
<!-- &#NNN;  decimal code point -->
<!-- &#xHHH; hexadecimal code point -->

CDATA 段

CDATA 段让你无需转义即可包含 <、>、&——适合代码和标记。CDATA 不能字面包含 ']]>';要嵌入它,按所示拆分段。CDATA 是字符数据,不被解析为元素。

xml
<?xml version="1.0"?>
<root>
  <script><![CDATA[
    function check(a, b) {
      if (a < b && b > 0) {
        return a & b;
      }
    }
  ]]></script>
  <!-- to include ]]> inside CDATA, split it -->
  <data><![CDATA[foo]]]]><![CDATA[>bar]]></data>
</root>

自定义实体(DTD)

内部实体在 DTD 中声明,在引用处展开。它们像文本宏,可引用其他实体。适合重复样板文本。注意:外部/通用实体可能是 XXE 安全风险。

xml
<?xml version="1.0"?>
<!DOCTYPE root [
  <!ENTITY company "Acme Corp.">
  <!ENTITY version "2.0">
  <!ENTITY copyright "Copyright © 2024 &company;">
]>
<root>
  <product>&company; Toolkit v&version;</product>
  <footer>&copyright;</footer>
</root>
<!-- Entities are expanded during parsing -->

属性中的转义

在属性值内必须把 & 转义为 &amp;,并转义用于界定值的引号(&quot; 或 &apos;)。< 也应转义。属性中的换行和制表符会被解析器规范化为空格。

xml
<?xml version="1.0"?>
<root>
  <link url="https://example.com?a=1&amp;b=2"/>
  <msg text="Say &quot;hello&quot; &amp; smile"/>
  <path value='C:\Users\name'/>
  <data value="&lt;tagged&gt;"/>
</root>

参数实体

参数实体(用 % 声明)仅在 DTD 内使用,用于构建可复用的内容模型。引用形式为 %name;(带分号)。通用实体(&name;)用于文档内容。

xml
<?xml version="1.0"?>
<!DOCTYPE root [
  <!ENTITY % inline " (#PCDATA | em | strong)* ">
  <!ELEMENT p %inline;>
  <!ELEMENT em %inline;>
  <!ELEMENT strong %inline;>
  <!ELEMENT root (p)*>
]>
<root>
  <p>Hello <em>world</em> and <strong>XML</strong>.</p>
</root>
05

DTD(文档类型定义)

内部 DTD

内部 DTD 在 DOCTYPE 的方括号内内联声明。它定义结构:存在哪些元素、它们的内容模型和属性。内部 DTD 自包含,但只适用于那一个文档。

xml
<?xml version="1.0"?>
<!DOCTYPE note [
  <!ELEMENT note (to, from, heading, body)>
  <!ELEMENT to (#PCDATA)>
  <!ELEMENT from (#PCDATA)>
  <!ELEMENT heading (#PCDATA)>
  <!ELEMENT body (#PCDATA)>
]>
<note>
  <to>Alice</to>
  <from>Bob</from>
  <heading>Reminder</heading>
  <body>Don't forget the meeting</body>
</note>

外部 DTD

外部 DTD 存放在单独的 .dtd 文件中,用 SYSTEM(私有)或 PUBLIC(公共标识符 + URI)引用。外部 DTD 让多个文档共享一份定义。SYSTEM 'file.dtd' 是最常见形式。

xml
<!-- File: note.dtd -->
<!ELEMENT note (to, from, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT body (#PCDATA)>

<!-- File: note.xml -->
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
  <to>Alice</to>
  <from>Bob</from>
  <body>Hello</body>
</note>

元素声明

内容模型用 ,(序列)、|(选择)、?(0-1)、*(0+)、+(1+)。#PCDATA 是文本;混合内容允许文本与元素交错。EMPTY 表示无内容;ANY 禁用检查(生产环境避免使用)。

xml
<!ELEMENT root (child1, child2*)>
<!-- content model operators -->
<!ELEMENT list (item+)>            <!-- one or more item -->
<!ELEMENT opt (item?)>             <!-- zero or one item   -->
<!ELEMENT any (item1 | item2)*>    <!-- choice, any number  -->
<!ELEMENT text (#PCDATA)>          <!-- parsed character data -->
<!ELEMENT mixed (#PCDATA | em)*>   <!-- mixed content -->
<!ELEMENT empty EMPTY>             <!-- no content -->
<!ELEMENT any ANY>                 <!-- any content -->

属性声明

ATTLIST 声明属性的类型和默认值。ID 是唯一标识符;IDREF/IDREFS 引用 ID(用于交叉链接)。NMTOKEN 是名称令牌。枚举类型列出允许值。默认值:#REQUIRED、#IMPLIED、#FIXED 或字面量。

xml
<!ATTLIST book
    id      ID         #REQUIRED
    category CDATA     #IMPLIED
    lang    NMTOKEN    "en"
    status  (draft|final) "draft"
    version CDATA      #FIXED "1.0">

<!-- Types: CDATA, ID, IDREF, IDREFS, NMTOKEN, NMTOKENS,
     NOTATION, enumerated (a|b|c) -->
<!ATTLIST link ref IDREF #REQUIRED>

实体声明

通用实体(&name;)在内容中展开;参数实体(%name;)在 DTD 中展开。外部已解析实体包含其他 XML;未解析实体(带 NDATA)指向非 XML 数据,通过 NOTATION 引用。外部实体可导致 XXE 注入。

xml
<!DOCTYPE root [
  <!-- internal general entity -->
  <!ENTITY name "Alice">
  <!-- external parsed entity -->
  <!ENTITY chap1 SYSTEM "chapter1.xml">
  <!-- unparsed entity (binary, e.g. image) -->
  <!ENTITY logo SYSTEM "logo.png" NDATA image>
  <!-- notation declaration for unparsed entities -->
  <!NOTATION image SYSTEM "image/png">
  <!-- parameter entity (DTD-only) -->
  <!ENTITY % block "(p | list)+">
]>
<root>&name;</root>

DTD 验证

验证检查文档是否匹配其 DTD(元素顺序、允许的属性、ID 唯一性)。标准 DOM 解析器如 minidom 默认不验证——使用 lxml(Python)、xmllint(命令行)或验证型解析器。

xml
<?xml version="1.0"?>
<!DOCTYPE root SYSTEM "schema.dtd">
<root>
  <item id="x1">valid</item>
</root>

<!-- Validate with Python: -->
<!-- import xml.dom.minidom as m
dom = m.parse("file.xml")
# minidom does NOT validate; use lxml: -->
<!-- from lxml import etree
dtd = etree.DTD("schema.dtd")
tree = etree.parse("file.xml")
print(dtd.validate(tree)) -->
06

XML Schema(XSD)

Schema 结构

XSD 本身是 XML 文档。根 <schema> 声明 XSD 命名空间和它所定义元素的目标命名空间。elementFormDefault='qualified' 把局部元素放入目标命名空间。

xml
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="https://example.com/order"
           xmlns:tns="https://example.com/order"
           elementFormDefault="qualified">

  <xs:element name="order" type="tns:OrderType"/>

  <xs:complexType name="OrderType">
    <xs:sequence>
      <xs:element name="customer" type="xs:string"/>
      <xs:element name="total" type="xs:decimal"/>
    </xs:sequence>
    <xs:attribute name="id" type="xs:ID" use="required"/>
  </xs:complexType>
</xs:schema>

简单类型

simpleType 用约束面(pattern、minInclusive、enumeration、length 等)限制基类型。简单类型只有文本内容和属性——无子元素。可在整个 schema 中复用。

xml
<xs:simpleType name="emailType">
  <xs:restriction base="xs:string">
    <xs:pattern value="[^@]+@[^@]+.[^@]+"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="ageType">
  <xs:restriction base="xs:integer">
    <xs:minInclusive value="0"/>
    <xs:maxInclusive value="120"/>
  </xs:restriction>
</xs:simpleType>

<xs:element name="email" type="tns:emailType"/>
<xs:element name="age" type="tns:ageType"/>

复杂类型

complexType 可包含子元素和属性。sequence 强制顺序;choice 允许选其一;all 允许任意顺序(每个最多一次)。组合器可嵌套以表达丰富结构。

xml
<xs:complexType name="AddressType">
  <xs:sequence>
    <xs:element name="street" type="xs:string"/>
    <xs:element name="city" type="xs:string"/>
    <xs:element name="zip" type="xs:string"/>
  </xs:sequence>
  <xs:attribute name="country" type="xs:string" default="US"/>
</xs:complexType>

<!-- Content model indicators: sequence, choice, all -->
<xs:complexType name="ContactType">
  <xs:choice>
    <xs:element name="phone" type="xs:string"/>
    <xs:element name="email" type="xs:string"/>
  </xs:choice>
</xs:complexType>

内置类型

XSD 提供丰富的类型层次:字符串、数字(integer、decimal、float)、日期/时间、boolean、anyURI、ID/IDREF、QName。每个都支持 length、pattern、enumeration 等约束面做进一步限制。

xml
<!-- Common XSD built-in types -->
<xs:element name="count"  type="xs:integer"/>
<xs:element name="price"  type="xs:decimal"/>
<xs:element name="flag"   type="xs:boolean"/>
<xs:element name="name"   type="xs:string"/>
<xs:element name="date"   type="xs:date"/>        <!-- 2024-01-15 -->
<xs:element name="time"   type="xs:time"/>        <!-- 13:45:00 -->
<xs:element name="dt"     type="xs:dateTime"/>    <!-- 2024-01-15T13:45:00 -->
<xs:element name="id"     type="xs:ID"/>
<xs:element name="ref"    type="xs:IDREF"/>
<xs:element name="uri"    type="xs:anyURI"/>

约束(约束面)

约束面约束简单类型:enumeration(允许值)、pattern(正则)、length/minLength/maxLength、min/maxInclusive/Exclusive、totalDigits、fractionDigits、whiteSpace(preserve/replace/collapse)。

xml
<xs:simpleType name="ColorType">
  <xs:restriction base="xs:string">
    <xs:enumeration value="red"/>
    <xs:enumeration value="green"/>
    <xs:enumeration value="blue"/>
  </xs:restriction>
</xs:simpleType>

<xs:simpleType name="PasswordType">
  <xs:restriction base="xs:string">
    <xs:minLength value="8"/>
    <xs:maxLength value="32"/>
    <xs:pattern value="[A-Za-z0-9!@#]+"/>
  </xs:restriction>
</xs:simpleType>

XSD 中的元素与属性

minOccurs/maxOccurs 控制基数(默认 1);maxOccurs='unbounded' 允许任意数量。属性 'use' 为 required/optional/prohibited,可带可选 default 或 fixed 值。匿名复杂类型内联定义。

xml
<xs:element name="product">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="name" type="xs:string"
                  minOccurs="1" maxOccurs="1"/>
      <xs:element name="tag" type="xs:string"
                  minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="sku" type="xs:string" use="required"/>
    <xs:attribute name="weight" type="xs:decimal" use="optional"
                  default="0.0"/>
  </xs:complexType>
</xs:element>
07

XPath

路径表达式

XPath 使用类路径表达式。'/' 从根选择;'//' 在任意深度选择后代。'*' 是任意元素的通配符;'@' 选择属性。以 '/' 开头的表达式是绝对的。

xml
<!-- Sample: <bookstore><book><title>..</title></book></bookstore> -->

/bookstore/book/title       <!-- absolute path -->
bookstore/book/title        <!-- relative path -->
//title                     <!-- any title, anywhere -->
/bookstore//title           <!-- title anywhere under bookstore -->
bookstore/*                 <!-- all children of bookstore -->
//@lang                     <!-- all lang attributes -->
//@*                        <!-- all attributes -->

谓词

[ ] 中的谓词过滤节点集。索引从 1 开始。last() 和 position() 指节点在其上下文中的位置。谓词可测试元素文本、属性(@name)或计算值。

xml
//book[1]                  <!-- first book -->
//book[last()]            <!-- last book -->
//book[position() <= 3]   <!-- first three books -->
//book[price > 25]        <!-- books with price > 25 -->
//book[@category='web']   <!-- books in the web category -->
//book[author='Smith']    <!-- books authored by Smith -->
//book[@lang][2]          <!-- 2nd book that has a lang attr -->

轴定义导航方向:child(默认)、descendant、parent(..)、ancestor、following-sibling、preceding-sibling、attribute(@)、self(.)、descendant-or-self(//)。多数有实践中常用的简写形式。

xml
child::book            <!-- default axis: children named book -->
descendant::title      <!-- all title descendants -->
parent::*              <!-- the parent element (shorthand: ..) -->
ancestor::section      <!-- all section ancestors -->
following-sibling::p   <!-- p elements after this one -->
preceding::item        <!-- items before this in document order -->
attribute::id          <!-- the id attribute (shorthand: @id) -->
self::node()           <!-- the current node (shorthand: .) -->

函数

XPath 1.0 内置函数:count、sum、string-length、contains、starts-with、normalize-space、name、concat、substring、round 等。XPath 2.0+ 大幅扩展函数库并增加类型。

xml
count(//book)                  <!-- number of book elements -->
string-length(//title)        <!-- length of first title's text -->
contains(//name, 'Ali')       <!-- true if name contains 'Ali' -->
starts-with(@id, 'b')         <!-- true if id starts with 'b' -->
normalize-space(//summary)    <!-- collapse whitespace -->
name(//*[1])                  <!-- name of the first element -->
concat(/a, /b)                <!-- concatenate strings -->
sum(//price)                  <!-- sum of all price values -->

运算符

XPath 用 = 表示相等(单个 =,不是 ==),用 'and'/'or' 做逻辑运算(不是 && / ||)。'div' 是除法,'mod' 是取模,因为 '/' 保留给路径。'|' 计算节点集的并集。

xml
<!-- Comparison: =, !=, <, >, <=, >= -->
//book[price = 29.99]
//book[price < 20]

<!-- Logical: and, or -->
//book[price > 10 and price < 30]
//book[@cat='a' or @cat='b']

<!-- Arithmetic: + - * div mod -->
//item[price * quantity > 100]
5 div 2        <!-- 2.5 -->
5 mod 2        <!-- 1 -->

<!-- Union: | -->
//book | //magazine

XPath 示例

这些模式组合路径、谓词和函数做实际查询。text() 选择文本节点;min() 需要 XPath 2.0+。带谓词的 //* 通配符是按属性在整个树中查找元素的常见方式。

xml
<!-- Select all book titles priced over 20 -->
//book[price > 20]/title/text()

<!-- The category of the cheapest book -->
//book[price = min(//book/price)]/@category

<!-- Authors of books in the 'programming' category -->
//book[@category='programming']/author

<!-- Every 2nd item in each list -->
//list/item[position() mod 2 = 0]

<!-- Elements with a specific attribute value -->
//*[@lang='fr']
08

XSLT 转换

基本转换

XSLT 样式表是 XML。模板用 XPath 匹配节点;apply-templates 递归处理选中的节点。value-of 提取文本。match='/' 模板最先在文档根上运行。

xml
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <html>
      <body>
        <h2>Book List</h2>
        <xsl:apply-templates select="bookstore/book"/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="book">
    <p><xsl:value-of select="title"/> - <xsl:value-of select="price"/></p>
  </xsl:template>
</xsl:stylesheet>

xsl:template 与 apply-templates

匹配模板在处理器访问匹配节点时触发;命名模板用 call-template 显式调用。不带 select 的 apply-templates 处理所有子节点,实现递归的规则驱动转换。

xml
<xsl:template match="book">
  <div class="book">
    <xsl:apply-templates select="title"/>
    <xsl:apply-templates select="author"/>
  </div>
</xsl:template>

<xsl:template match="title">
  <h3><xsl:apply-templates/></h3>
</xsl:template>

<!-- named template (like a function) -->
<xsl:template name="divider">
  <hr/>
</xsl:template>

xsl:value-of 与 xsl:for-each

xsl:for-each 迭代节点集,改变上下文节点。对于扁平输出它常比模板简单,但过度使用会使样式表过程化。递归结构优先用模板和 apply-templates。

xml
<xsl:template match="/">
  <ul>
    <xsl:for-each select="bookstore/book">
      <li>
        <xsl:value-of select="title"/>
        (<xsl:value-of select="@category"/>)
      </li>
    </xsl:for-each>
  </ul>
</xsl:template>

<!-- value-of outputs the string value of an expression -->

xsl:if 与 xsl:choose

xsl:if 没有 else——多路分支用 xsl:choose。'test' 表达式遵循 XPath 布尔规则:非空节点集为真,空为假,非零数字为真。

xml
<xsl:for-each select="bookstore/book">
  <xsl:if test="price > 25">
    <premium><xsl:value-of select="title"/></premium>
  </xsl:if>

  <xsl:choose>
    <xsl:when test="@category='fiction'">F</xsl:when>
    <xsl:when test="@category='programming'">P</xsl:when>
    <xsl:otherwise>?</xsl:otherwise>
  </xsl:choose>
</xsl:for-each>

<!-- test uses XPath; non-empty node-sets and non-zero numbers are true -->

xsl:sort 与 xsl:variable

xsl:sort 按一个或多个键对当前节点集重新排序;data-type 为 'text' 或 'number'。变量(xsl:variable)不可变且有作用域。记得在样式表 XML 的属性值内把 < 写成 &lt;。

xml
<xsl:variable name="max-price" select="50"/>

<xsl:for-each select="bookstore/book[price &lt; $max-price]">
  <xsl:sort select="price" data-type="number" order="descending"/>
  <xsl:sort select="title" order="ascending"/>
  <p>
    <xsl:value-of select="title"/>: <xsl:value-of select="price"/>
  </p>
</xsl:for-each>

<!-- Note: in XSLT, < must be written &lt; inside test attributes -->

xsl:apply-templates 与模式

模式(mode)让一组源节点以不同方式处理(如目录 vs. 完整条目)。template 和 apply-templates 上的 mode 属性必须匹配。无 mode 时,只应用无 mode 的模板。

xml
<!-- A template can have a 'mode' for different outputs -->
<xsl:template match="book" mode="list">
  <li><xsl:value-of select="title"/></li>
</xsl:template>

<xsl:template match="book" mode="detail">
  <div><h3><xsl:value-of select="title"/></h3>
       <p><xsl:value-of select="author"/></p></div>
</xsl:template>

<!-- Invoke a specific mode -->
<xsl:apply-templates select="bookstore/book" mode="list"/>
<xsl:apply-templates select="bookstore/book" mode="detail"/>
09

XQuery

FLWOR 表达式

FLWOR 是 XQuery 的核心构造:for 把变量绑定到序列项,let 绑定计算值,where 过滤,order by 排序,return 塑造输出。类似 SQL 的 SELECT-FROM-WHERE。

xml
for $b in /bookstore/book
let $discount := $b/price * 0.9
where $b/price > 20
order by $b/price descending
return
  <book>
    <title>{ $b/title/text() }</title>
    <salePrice>{ $discount }</salePrice>
  </book>

<!-- FLWOR = For, Let, Where, Order by, Return -->

路径表达式

XQuery 直接嵌入 XPath。doc('file.xml') 加载文档;collection('uri') 查询多个文档。路径表达式返回节点序列,FLWOR 表达式可迭代它们。

xml
(: XQuery uses XPath for navigation :)
/bookstore/book/title/text()
/bookstore/book[@category='web']
//author[contains(., 'Smith')]

(: Use doc() to open an external document :)
doc('catalog.xml')//book[price < 30]

(: collection() queries a set of documents :)
collection('/db/books')//title

谓词与连接

多个 'for' 子句构成连接(类似 SQL JOIN)。XQuery 通过迭代组合并用 where 过滤来执行连接。大数据集上,用谓词或键显式连接比嵌套循环更高效。

xml
(: Join books to authors by id :)
for $b in doc('books.xml')//book
for $a in doc('authors.xml')//author
where $b/@author-id = $a/@id
return
  <entry>
    <title>{ $b/title/text() }</title>
    <author>{ $a/name/text() }</author>
  </entry>

(: Equivalent join with a predicate :)
for $b in doc('books.xml')//book
return
  <entry>{ $b/title, $b/@author-id }</entry>

条件与量化表达式

XQuery 有 if-then-else(else 必填)、量化表达式(some/every ... satisfies)和 typeswitch 按节点类型分支。条件使用 XPath 风格的布尔语义。

xml
(: if-then-else :)
for $b in //book
return
  if ($b/price > 30) then <expensive>{ $b/title }</expensive>
  else <cheap>{ $b/title }</cheap>

(: quantified expressions :)
some $a in //author satisfies contains($a, 'Smith')
every $p in //price satisfies $p > 0

(: typeswitch for type-based dispatch :)
typeswitch($node)
  case element(book) return "book"
  case element(author) return "author"
  default return "other"

函数

XQuery 复用 XPath 函数并新增许多(string-join、distinct-values、avg、min、max)。用户函数在模块中声明,带参数和返回类型,按约定放在 local: 命名空间。

xml
(: Built-in functions :)
concat('a', 'b', 'c')
string-join(//title, ', ')
count(//book)
distinct-values(//book/@category)
avg(//book/price)
max(//book/price)

(: User-defined function :)
declare function local:discount($price as xs:decimal)
  as xs:decimal {
  $price * 0.9
};

local:discount(29.99)

构造 XML

XQuery 用直接构造器(字面标签加 { } 嵌入表达式)或计算构造器(element name { ... })构建 XML。花括号求值 enclosed 表达式;属性可内联嵌入值。

xml
(: Direct element construction :)
<result>
  <count>{ count(//book) }</count>
  <books>
    { for $b in //book return <book title="{ $b/title/text() }"/> }
  </books>
</result>

(: Computed constructors :)
element result {
  attribute total { count(//book) },
  for $b in //book
  return element book { $b/title/text() }
}

(: Enclosed expressions use { } to embed values -->
10

DOM 解析

加载 XML(JavaScript)

DOMParser 在浏览器中将 XML 字符串转为 DOM 树;解析错误以 <parsererror> 元素出现而非异常。Node.js 无内置 XML DOM——使用 xmldom、@xmldom/xmldom 或流式解析器。

xml
// Browser: parse an XML string
const parser = new DOMParser();
const doc = parser.parseFromString(xmlString, "application/xml");

const errorNode = doc.querySelector("parsererror");
if (errorNode) {
  console.error("Parse error:", errorNode.textContent);
}

const books = doc.getElementsByTagName("book");
for (const book of books) {
  console.log(book.getAttribute("id"));
}

// Node.js: use xmldom or fast-xml-parser instead

DOM 遍历

DOM API 暴露 documentElement、children、childNodes、nextSibling 用于遍历。nodeType 1 是元素,3 是文本,8 是注释。document.evaluate 在浏览器运行 XPath(Node 需 xpath 库)。

xml
// JavaScript DOM API (also works for XML documents)
const root = doc.documentElement;        // <bookstore>
const firstBook = root.children[0];      // first <book>
const titles = doc.getElementsByTagName("title");

// walk children
let node = root.firstChild;
while (node) {
  if (node.nodeType === 1) {             // Element node
    console.log(node.tagName);
  }
  node = node.nextSibling;
}

// XPath against the DOM
const it = doc.evaluate("//book[@id='b1']", doc, null,
                        XPathResult.ANY_TYPE, null);
const found = it.iterateNext();

修改 DOM

createElement/setAttribute/appendChild 构建并附加节点;textContent 设置文本。XMLSerializer 将 DOM 转回字符串。DOM 是实时的——修改立即反映到所有引用中。

xml
const book = doc.createElement("book");
book.setAttribute("id", "b3");

const title = doc.createElement("title");
title.textContent = "New Book";
book.appendChild(title);

root.appendChild(book);                  // add to tree

// change an existing element
const price = doc.querySelector("book:nth-child(2) price");
price.textContent = "19.99";
price.setAttribute("currency", "EUR");

// serialize back to a string
const xml = new XMLSerializer().serializeToString(doc);

创建元素

minidom 可从零构建文档:createDocument 建根,createElement/setAttribute/createTextNode 建内容,appendChild 连接。toprettyxml 带缩进序列化。minidom 简单但大树较慢。

xml
// Python: xml.dom.minidom
from xml.dom import minidom

impl = minidom.getDOMImplementation()
doc = impl.createDocument(None, "root", None)
root = doc.documentElement

item = doc.createElement("item")
item.setAttribute("id", "1")
item.appendChild(doc.createTextNode("Hello"))
root.appendChild(item)

print(doc.toprettyxml(encoding="UTF-8").decode())

Java DOM 解析

Java 的 JAXP DOM 通过 DocumentBuilder 把整个文档载入内存。setNamespaceAware(true) 保留命名空间前缀。DOM 便于随机访问但大文件内存占用高——此时优先 StAX/SAX。

xml
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;

DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
f.setNamespaceAware(true);
Document doc = f.newDocumentBuilder().parse(new File("data.xml"));

Element root = doc.getDocumentElement();
NodeList books = root.getElementsByTagName("book");
for (int i = 0; i < books.getLength(); i++) {
  Element b = (Element) books.item(i);
  String id = b.getAttribute("id");
  String title = b.getElementsByTagName("title")
                      .item(0).getTextContent();
  System.out.println(id + ": " + title);
}

lxml(Python)

lxml 是 libxml2 的快速、功能丰富的 Python 绑定。支持完整 XPath、XSLT、验证(DTD/XSD/RelaxNG)和增量解析。其 ElementPath(find/findall)是 XPath 子集;用 xpath() 跑完整 XPath。

xml
from lxml import etree

# parse and validate against a schema
tree = etree.parse("data.xml")
root = tree.getroot()

# XPath with namespaces
ns = {"bk": "https://example.com/books"}
for book in root.xpath("//bk:book", namespaces=ns):
    print(book.get("id"), book.find("bk:title", namespaces=ns).text)

# build an element tree
root = etree.Element("root")
child = etree.SubElement(root, "item", id="1")
child.text = "Hello"
print(etree.tostring(root, pretty_print=True, encoding="unicode"))
11

SAX 解析

SAX 处理器(Python)

SAX 是事件驱动流式的:解析器在读时调用 startElement、characters、endElement,从不构建完整树。对大文件内存高效。你必须自己跟踪状态(如当前元素)。

xml
import xml.sax

class BookHandler(xml.sax.ContentHandler):
    def startElement(self, name, attrs):
        if name == "book":
            print("Book id:", attrs.getValue("id"))
        self.current = name

    def characters(self, content):
        if self.current == "title":
            print("Title:", content.strip())

    def endElement(self, name):
        self.current = None

xml.sax.parse("books.xml", BookHandler())

SAX 事件方法

ContentHandler 回调标记文档和元素边界。characters() 可能把单个文本节点拆成多次调用——累积到缓冲区。startPrefixMapping/endPrefixMapping 报告命名空间声明。

xml
class Handler(xml.sax.ContentHandler):
    def startDocument(self):
        print("parsing started")

    def endDocument(self):
        print("parsing done")

    def startElement(self, name, attrs):
        # attrs: get via attrs.getValue('id') or attrs['id']
        pass

    def endElement(self, name):
        pass

    def characters(self, text):
        # may be called multiple times for one text node
        pass

    def startPrefixMapping(self, prefix, uri):
        print("namespace:", prefix, uri)

startElement / endElement

跟踪元素路径(栈)是流式处理中知道自己位置的标淮方式。缓冲 characters() 输出并在 endElement 中刷新,因为 characters 对一个文本节点可能触发多次。

xml
class Handler(xml.sax.ContentHandler):
    def __init__(self):
        self.path = []
        self.text = []

    def startElement(self, name, attrs):
        self.path.append(name)
        self.text = []
        if "id" in attrs:
            print("/".join(self.path), "id =", attrs["id"])

    def endElement(self, name):
        if self.path[-1] == "title":
            print("title:", "".join(self.text).strip())
        self.path.pop()

    def characters(self, content):
        self.text.append(content)

Java SAX 解析器

Java SAX 继承 DefaultHandler。startElement 接收命名空间 URI、本地名、限定名和 Attributes 对象。characters(ch, start, len) 提供 char 切片——要缓冲,因为文本节点可能跨多次调用。

xml
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.*;
import javax.xml.parsers.*;

class BookHandler extends DefaultHandler {
    private StringBuilder buf = new StringBuilder();

    public void startElement(String uri, String local,
                             String qName, Attributes attrs) {
        buf.setLength(0);
        if ("book".equals(qName))
            System.out.println("id=" + attrs.getValue("id"));
    }

    public void characters(char[] ch, int start, int len) {
        buf.append(ch, start, len);
    }

    public void endElement(String uri, String local, String qName) {
        if ("title".equals(qName))
            System.out.println("title=" + buf);
    }
}
// SAXParserFactory.newInstance().newSAXParser().parse(file, new BookHandler());

SAX 与 DOM 对比

SAX 以恒定内存流式传输事件,适合大文件和单遍提取。DOM 加载整棵树,支持 XPath 和随机修改,但内存与文件大小成正比。按访问模式和文件大小选择。

xml
<!-- SAX: event-driven, streaming, low memory
     DOM:  full tree in memory, random access

     SAX is best for:
       - large files that won't fit in memory
       - read-only filtering / extraction
       - simple state machines over the document

     DOM is best for:
       - random access to many parts of the tree
       - repeated queries / XPath
       - editing and re-serializing -->

<!-- Memory: SAX ~ O(1); DOM ~ O(file size) -->
<!-- Speed:  SAX often faster for one-pass scans -->

错误处理(SAX)

ErrorHandler 接收警告、可恢复错误和致命错误。默认致命错误抛异常并停止解析。实现 error() 可记录非致命验证问题而不中止整个解析。

xml
import xml.sax

class Handler(xml.sax.ContentHandler):
    pass

# ErrorHandler: warning, error, fatalError
class ErrHandler(xml.sax.ErrorHandler):
    def error(self, exception):
        print("ERROR:", exception)
    def fatalError(self, exception):
        print("FATAL:", exception)
        raise exception
    def warning(self, exception):
        print("WARN:", exception)

parser = xml.sax.make_parser()
parser.setContentHandler(Handler())
parser.setErrorHandler(ErrHandler())
parser.parse("data.xml")
12

StAX(拉解析)

StAX 游标 API

StAX 是拉解析器:你的代码用 next() 拉取下一个事件,所以你控制循环。游标 API(XMLStreamReader)逐事件推进。比 DOM 更快更低内存,比 SAX 的推模型更灵活。

xml
import javax.xml.stream.*;
import java.io.*;

XMLInputFactory factory = XMLInputFactory.newInstance();
// defend against XXE
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);

XMLStreamReader r = factory.createXMLStreamReader(new FileInputStream("data.xml"));
while (r.hasNext()) {
    int event = r.next();
    if (event == XMLStreamConstants.START_ELEMENT) {
        System.out.println("start: " + r.getLocalName());
    } else if (event == XMLStreamConstants.END_ELEMENT) {
        System.out.println("end: " + r.getLocalName());
    }
}
r.close();

XMLStreamReader 事件

事件类型包括 START_ELEMENT、END_ELEMENT、CHARACTERS、COMMENT、PROCESSING_INSTRUCTION、START/END_DOCUMENT。getText() 返回当前文本;isWhiteSpace() 过滤缩进。游标每次 next() 前进一个事件。

xml
XMLStreamReader r = factory.createXMLStreamReader(in);
while (r.hasNext()) {
    switch (r.next()) {
        case XMLStreamConstants.START_ELEMENT:
            // r.getLocalName(), r.getAttributeValue(i), r.getNamespaceURI()
            break;
        case XMLStreamConstants.END_ELEMENT:
            break;
        case XMLStreamConstants.CHARACTERS:
            if (!r.isWhiteSpace()) {
                String text = r.getText();
            }
            break;
        case XMLStreamConstants.START_DOCUMENT:
            break;
        case XMLStreamConstants.END_DOCUMENT:
            break;
        case XMLStreamConstants.PROCESSING_INSTRUCTION:
            break;
    }
}

读取元素与属性

getAttributeValue(namespaceURI, localName) 按名读属性(无命名空间传 null)。getElementText() 是便利方法,读取纯文本元素文本并越过其 END_ELEMENT——比自己缓冲 characters 更干净。

xml
while (r.hasNext()) {
    int event = r.next();
    if (event == XMLStreamConstants.START_ELEMENT
            && "book".equals(r.getLocalName())) {
        String id = r.getAttributeValue(null, "id");
        String category = r.getAttributeValue(null, "category");
        System.out.println("Book " + id + " (" + category + ")");
    }
    if (event == XMLStreamConstants.START_ELEMENT
            && "title".equals(r.getLocalName())) {
        String title = r.getElementText();   // consumes until END_ELEMENT
        System.out.println("Title: " + title);
    }
}

StAX 写入器

XMLStreamWriter 顺序构建 XML。writeStartDocument、writeStartElement/writeEndElement、writeAttribute、writeCharacters、writeNamespace 必须配平。最后 flush/close 确保输出完整。

xml
XMLOutputFactory out = XMLOutputFactory.newInstance();
XMLStreamWriter w = out.createXMLStreamWriter(System.out, "UTF-8");

w.writeStartDocument("UTF-8", "1.0");
w.writeStartElement("books");
w.writeNamespace("bk", "https://example.com/books");

w.writeStartElement("bk", "book", "https://example.com/books");
w.writeAttribute("id", "b1");
w.writeStartElement("bk", "title", "https://example.com/books");
w.writeCharacters("XML Guide");
w.writeEndElement();   // title
w.writeEndElement();   // book

w.writeEndElement();   // books
w.writeEndDocument();
w.close();

StAX 与 SAX 对比

SAX 向你的处理器推事件;StAX 让你按需拉事件。StAX 的拉模型更易跳过子树和管理状态,并增加了写入 API。两者都不构建完整 DOM 树,适合大文档。

xml
<!-- StAX: PULL model  (client calls next())
     SAX:  PUSH model  (parser calls your handlers)

     StAX advantages:
       - client controls parsing (easy to skip sections)
       - simpler state management for many tasks
       - supports both reading and writing
       - can pause/resume naturally

     SAX advantages:
       - slightly lower overhead in pure streaming
       - mature, widely available
       - filters/chains via XMLFilter

     Both are streaming and memory-efficient. -->

事件读取器(迭代器 API)

迭代器 API(XMLEventReader)返回可 peek 和消费的 XMLEvent 对象。比游标 API 更面向对象,更易做过滤或拆分,但因事件对象分配而稍重。

xml
import javax.xml.stream.*;
import javax.xml.stream.events.*;

XMLEventReader reader = XMLInputFactory.newInstance()
        .createXMLEventReader(in);

while (reader.hasNext()) {
    XMLEvent e = reader.peek();              // look without consuming
    if (e.isStartElement()) {
        StartElement se = e.asStartElement();
        System.out.println(se.getName());
    }
    XMLEvent consumed = reader.nextEvent();   // consume it
    if (consumed.isEndElement()) {
        System.out.println("closed");
    }
}
reader.close();
13

XML 与 JSON 转换

XML 转 JSON(JavaScript)

fast-xml-parser 在 Node.js 中把 XML 转 JSON。属性加前缀(默认 @_),重复元素变数组。该库是纯 JavaScript 且快速,有选项控制数组、数字和布尔值。

xml
// Node.js with fast-xml-parser
const { XMLParser } = require("fast-xml-parser");

const xml = "<book id='b1'><title>XML</title><price>29.99</price></book>";
const parser = new XMLParser({
  ignoreAttributes: false,
  attributeNamePrefix: "@_",
});

const json = parser.parse(xml);
console.log(json.book["@_id"]);   // b1
console.log(json.book.title);     // XML
console.log(json.book.price);     // 29.99

JSON 转 XML(JavaScript)

XMLBuilder 反向转换。@_ 前缀标记属性,嵌套对象变子元素。数组产生重复元素。XML 的单根规则意味着顶层 JSON 对象必须映射到一个根元素。

xml
const { XMLBuilder } = require("fast-xml-parser");

const json = {
  book: {
    "@_id": "b1",
    title: "XML Guide",
    price: 29.99,
  },
};

const builder = new XMLBuilder({
  format: true,
  attributeNamePrefix: "@_",
  ignoreAttributes: false,
});

const xml = builder.build(json);
console.log(xml);
// <book id="b1">
//   <title>XML Guide</title>
//   <price>29.99</price>
// </book>

Python xmltodict

xmltodict 把 XML 当作 dict/list 结构:属性用 '@name',文本用 '#text'。parse() 读 XML,unparse() 写 XML。适合快速往返但复杂 schema 会丢失顺序和某些边界情况。

xml
import xmltodict, json

xml = """<book id="b1"><title>XML</title><price>29.99</price></book>"""

# XML -> dict
data = xmltodict.parse(xml)
print(data["book"]["@id"])      # b1
print(data["book"]["title"])    # XML

# dict -> JSON
print(json.dumps(data, indent=2))

# dict -> XML
xml_out = xmltodict.unparse({"book": {"@id": "b2", "title": "New"}})
print(xml_out)

属性与文本处理

XML 转 JSON 需决定属性(@_前缀)、文本(#text)和混合内容的处理。混合内容——文本与子元素交错——无法干净映射到 JSON,常丢失保真度。选定约定并保持一致。

xml
<!-- XML with mixed attributes and text -->
<product sku="A1" stock="5">Widget<note>sale</note></product>

// fast-xml-parser output:
// {
//   product: {
//     "@_sku": "A1",
//     "@_stock": "5",
//     "#text": "Widget",
//     note: "sale"
//   }
// }

// Round-trip carefully: mixed content (text + elements)
// is hard to represent losslessly in JSON -->

转换挑战

常见转换陷阱:单子元素 vs. 多子元素(数组 vs. 字符串)、元素顺序、命名空间前缀(它们是别名)、单根约束、原始类型(XML 无原生数字或布尔值)。记录你的约定。

xml
<!-- 1. Repeated elements -> array -->
<list><item>a</item><item>b</item></list>
// { list: { item: ["a", "b"] } }   (or single string if only one)

<!-- 2. Order is significant in XML but not in JSON objects -->
<a><x/><y/></a>   vs   <a><y/><x/></a>   // may differ

<!-- 3. Namespaces -->
<ns:elem xmlns:ns="http://x"/>   // prefixes are aliases

<!-- 4. Single root: JSON top-level must be one object -->
<!-- 5. Numbers/booleans: XML is all text; typing is heuristic -->

往返示例

往返(XML -> JSON -> XML)对结构良好的数据可行,但可能丢失注释、处理指令、CDATA 边界和确切空白。要无损编辑,改用 DOM/树 API 而非 JSON 转换。

xml
// Parse, modify, and re-serialize with fast-xml-parser
const { XMLParser, XMLBuilder } = require("fast-xml-parser");

const opts = { ignoreAttributes: false, attributeNamePrefix: "@_" };
const parser = new XMLParser(opts);
const builder = new XMLBuilder({ ...opts, format: true });

const obj = parser.parse(xmlString);
// mutate
obj.book.price = 19.99;
obj.book["@_onSale"] = "true";

const newXml = builder.build(obj);
console.log(newXml);
14

RSS 与 Atom Feed

RSS 2.0 结构

RSS 2.0 有 <rss> 根和一个 <channel>。必需 channel 字段:title、link、description。每个 <item> 有 title、link、description、guid、pubDate。日期遵循 RFC 822(如 'Wed, 02 Oct 2024 13:00:00 GMT')。

xml
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>My Blog</title>
    <link>https://example.com</link>
    <description>Recent posts</description>
    <language>en-us</language>
    <pubDate>Wed, 02 Oct 2024 13:00:00 GMT</pubDate>
    <item>
      <title>First Post</title>
      <link>https://example.com/1</link>
      <description>A short summary</description>
      <pubDate>Wed, 02 Oct 2024 12:00:00 GMT</pubDate>
      <guid>https://example.com/1</guid>
    </item>
  </channel>
</rss>

RSS Channel 与 Item

Channel 级字段描述 feed(title、link、description、language、ttl、image)。Item 字段描述每条目;enclosure 支持播客/媒体(URL、字节长度、MIME 类型)。category 可带 domain 属性。

xml
<channel>
  <title>Site Title</title>
  <link>https://example.com</link>
  <description>What the feed is about</description>
  <category>Technology</category>
  <ttl>60</ttl>                          <!-- cache minutes -->
  <image>
    <url>https://example.com/logo.png</url>
    <title>Site Title</title>
    <link>https://example.com</link>
  </image>
  <item>
    <title>Post Title</title>
    <link>https://example.com/post</link>
    <description>Summary text</description>
    <author>[email protected]</author>
    <category domain="https://example.com/tags">xml</category>
    <enclosure url="https://example.com/p.mp3" length="12345"
               type="audio/mpeg"/>
  </item>
</channel>

Atom Feed

Atom(RFC 4287)用 Atom 命名空间的 <feed> 根。必需:id、title、updated。条目需 id、title、updated。ID 应全局唯一(URN 或 tag: URI)。日期用 RFC 3339(ISO 8601)带时区。

xml
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Example Feed</title>
  <link href="https://example.com/feed.atom" rel="self"/>
  <link href="https://example.com"/>
  <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
  <updated>2024-10-02T13:00:00Z</updated>
  <author><name>Jane Doe</name></author>
  <entry>
    <title>Atom Entry</title>
    <id>tag:example.com,2024:/post/1</id>
    <updated>2024-10-02T12:00:00Z</updated>
    <summary>A short summary</summary>
    <content type="html">&lt;p&gt;Full post body&lt;/p&gt;</content>
  </entry>
</feed>

Atom Entry 细节

Atom 条目支持丰富内容:通过 type 属性选 text、html 或 xhtml。link 用 rel 属性(alternate、self、enclosure、related)。category 有 term/scheme/label。published 是原始日期;updated 是最后修改。

xml
<entry>
  <title type="text">Post Title</title>
  <id>tag:example.com,2024:/posts/42</id>
  <updated>2024-10-02T12:00:00Z</updated>
  <published>2024-10-01T09:00:00Z</published>
  <author>
    <name>Jane Doe</name>
    <email>[email protected]</email>
    <uri>https://example.com/jane</uri>
  </author>
  <link href="https://example.com/posts/42" rel="alternate"/>
  <category term="xml" scheme="https://example.com/tags" label="XML"/>
  <summary type="text">A teaser.</summary>
  <content type="xhtml">
    <div xmlns="http://www.w3.org/1999/xhtml"><p>Body</p></div>
  </content>
</entry>

常见 Feed 命名空间

RSS 常用模块扩展:Dublin Core(dc:creator、dc:date)、content(content:encoded 放完整 HTML)、Media RSS(media:content),以及帮阅读器找到 feed URL 的 atom:link rel='self'。命名空间让 feed 混合词汇表。

xml
<?xml version="1.0"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:media="http://search.yahoo.com/mrss/"
     xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Feed</title>
    <link>https://example.com</link>
    <description>desc</description>
    <atom:link href="https://example.com/feed.xml" rel="self"
               type="application/rss+xml"/>
    <item>
      <title>Post</title>
      <dc:creator>Jane Doe</dc:creator>
      <content:encoded><![CDATA[<p>HTML body</p>]]></content:encoded>
      <media:content url="https://example.com/img.png" medium="image"/>
    </item>
  </channel>
</rss>

Feed 验证

用 W3C Feed Validator 检查 RSS/Atom。常见错误:缺少必需字段、日期格式错误、ID 不唯一、无效 XML(未转义 &)、缺少 self 链接。有效 feed 确保聚合器可靠解析。

xml
<!-- RSS: dates must be RFC 822, e.g. -->
<pubDate>Wed, 02 Oct 2024 13:00:00 GMT</pubDate>

<!-- Atom: dates must be RFC 3339, e.g. -->
<updated>2024-10-02T13:00:00Z</updated>

<!-- Validate online with https://validator.w3.org/feed/ -->
<!-- Required RSS channel fields: title, link, description -->
<!-- Required Atom feed fields: id, title, updated -->
<!-- GUIDs/IDs should be unique and permanent -->
15

SVG 与 XML

SVG 文档结构

SVG 是矢量图形的 XML 词汇表。根 <svg> 声明 SVG 命名空间和坐标系 viewBox。所有图形元素(rect、circle、text)都是带表现属性的 XML 元素。

xml
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="200" height="200" viewBox="0 0 200 200">
  <rect x="10" y="10" width="180" height="180"
        fill="#005FAD" stroke="black" stroke-width="2"/>
  <circle cx="100" cy="100" r="40" fill="white"/>
  <text x="100" y="105" text-anchor="middle">SVG</text>
</svg>
<!-- SVG is an XML application; it must be well-formed -->

SVG 命名空间与引入

SVG 元素属于 http://www.w3.org/2000/svg 命名空间。xlink 用于 href 引用(SVG 1.1);SVG 2 优先用普通 href。defs 存放可复用的渐变、图案和符号,通过 url(#id) 引用。

xml
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     version="1.1">
  <defs>
    <linearGradient id="g1">
      <stop offset="0%" stop-color="red"/>
      <stop offset="100%" stop-color="blue"/>
    </linearGradient>
  </defs>
  <rect width="100" height="100" fill="url(#g1)"/>
</svg>

SVG 形状

SVG 形状元素:rect(用 rx/ry 做圆角)、circle、ellipse、line、polyline(开放)、polygon(闭合)。坐标用 viewBox 定义的用户单位。stroke 和 fill 是常见表现属性。

xml
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="200">
  <rect x="10" y="10" width="80" height="50" rx="10" fill="teal"/>
  <circle cx="150" cy="60" r="40" fill="orange"/>
  <ellipse cx="220" cy="60" rx="50" ry="20" fill="purple"/>
  <line x1="10" y1="120" x2="290" y2="120" stroke="black"/>
  <polyline points="10,180 50,140 90,180 130,140" fill="none" stroke="red"/>
  <polygon points="200,180 250,140 280,200" fill="green"/>
</svg>

SVG 文本与路径

SVG 文本用 font-family、font-size、font-weight、font-style。tspan 允许在文本元素内混合样式。path 元素的 'd' 属性用命令:M(移动)、L(直线)、C(三次贝塞尔)、Q(二次)、A(弧)、Z(闭合路径)。

xml
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="150">
  <text x="10" y="40" font-family="Arial" font-size="24"
        font-weight="bold" fill="navy">Hello SVG</text>

  <text x="10" y="80">
    <tspan font-size="16">Normal</tspan>
    <tspan font-size="16" font-style="italic"> italic</tspan>
  </text>

  <path d="M10 100 L 100 100 L 55 140 Z"
        fill="none" stroke="black" stroke-width="2"/>
</svg>
<!-- Path commands: M=move, L=line, H/V, C=cubic, Q=quad, A=arc, Z=close -->

SVG 分组与复用

<g> 元素为共享样式和变换分组。<use> 通过 id 引用元素(SVG 2 用 href='#id',1.1 用 xlink:href)并在新位置克隆。<symbol> 定义只能通过 <use> 渲染的可复用图形。

xml
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  <g id="node" stroke="black" fill="lightblue">
    <circle cx="50" cy="50" r="20"/>
    <circle cx="100" cy="50" r="20"/>
  </g>

  <use href="#node" x="0" y="100"/>
  <use href="#node" x="0" y="150" opacity="0.5"/>

  <symbol id="star" viewBox="0 0 24 24">
    <polygon points="12,2 15,9 22,9 17,14 19,21 12,17 5,21 7,14 2,9 9,9"/>
  </symbol>
  <use href="#star" width="24" height="24"/>
</svg>

SVG 验证

SVG 文档可对照 W3C DTD 或 XSD 验证。常见问题:忘记 SVG 命名空间、未声明前缀、属性放错命名空间、路径数据格式错误。xmllint 是标准命令行验证器。

xml
<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <rect width="100" height="100"/>
</svg>

<!-- Validate with xmllint: -->
<!-- xmllint --noout --valid file.svg        (DTD) -->
<!-- xmllint --noout --schema svg11.xsd file.svg   (XSD) -->

<!-- Common errors: wrong namespace, undeclared prefixes,
     text outside a text element, invalid path data -->
16

SOAP 与 WSDL

SOAP 信封

SOAP 消息是 XML 文档,根为 SOAP 命名空间的 <Envelope>。可选 <Header> 携带元数据(认证、路由);必需 <Body> 持有实际消息载荷。SOAP 1.1 和 1.2 用不同命名空间。

xml
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <auth xmlns="https://example.com/auth">
      <token>abc123</token>
    </auth>
  </soap:Header>
  <soap:Body>
    <m:GetPrice xmlns:m="https://example.com/pricing">
      <m:Item>Apples</m:Item>
    </m:GetPrice>
  </soap:Body>
</soap:Envelope>
<!-- Envelope is the root; Header is optional, Body is required -->

SOAP Header

Header 携带带外信息如认证、寻址(WS-Addressing)、事务。soap:mustUnderstand='1' 告诉接收方必须处理此 Header,否则返回 SOAP fault——这强制契约遵从。

xml
<soap:Header xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <wsa:Action xmlns:wsa="http://www.w3.org/2005/08/addressing">
    http://example.com/GetPrice
  </wsa:Action>
  <wsa:MessageID>urn:uuid:1234</wsa:MessageID>
  <auth xmlns="https://example.com/auth"
        soap:mustUnderstand="1">
    <token>secret</token>
  </auth>
</soap:Header>
<!-- mustUnderstand="1" forces the receiver to process the header -->

SOAP Body 与 Fault

Body 承载请求或响应载荷。出错时,Body 含 <Fault>,带 faultcode(Client/Server)、人可读 faultstring 和可选 detail。SOAP 1.2 重命名这些元素并增加 Code/Reason 结构。

xml
<!-- Request body -->
<soap:Body>
  <m:GetPrice xmlns:m="https://example.com/pricing">
    <m:Item>Apples</m:Item>
  </m:GetPrice>
</soap:Body>

<!-- Fault (error response) -->
<soap:Body>
  <soap:Fault>
    <faultcode>soap:Client</faultcode>
    <faultstring>Unknown item: Apples</faultstring>
    <detail>
      <m:Error xmlns:m="https://example.com/pricing">
        <code>404</code>
      </m:Error>
    </detail>
  </soap:Fault>
</soap:Body>

WSDL 结构

WSDL 描述 Web 服务:<types>(XSD schema)、<message>(输入/输出载荷)、<portType>(接口——操作及其消息)、<binding>(协议/编码)、<service>(端点 URL)。WSDL 2.0 简化为接口和绑定。

xml
<?xml version="1.0"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
             xmlns:xs="http://www.w3.org/2001/XMLSchema"
             targetNamespace="https://example.com/pricing">
  <types>
    <xs:schema targetNamespace="https://example.com/pricing">
      <xs:element name="GetPriceRequest" type="xs:string"/>
      <xs:element name="GetPriceResponse" type="xs:decimal"/>
    </xs:schema>
  </types>
  <message name="GetPriceInput">
    <part name="item" element="tns:GetPriceRequest"/>
  </message>
  <portType name="PricingPort">
    <operation name="GetPrice">
      <input message="tns:GetPriceInput"/>
      <output message="tns:GetPriceOutput"/>
    </operation>
  </portType>
</definitions>

WSDL 绑定与服务

Binding 把 portType 映射到具体协议(HTTP 上的 SOAP)和样式(document/literal 是现代默认)。service 元素列出端点(soap:address)。Document/literal 把载荷包在 schema 定义的元素中。

xml
<binding name="PricingSOAP" type="tns:PricingPort">
  <soap:binding style="document"
                transport="http://schemas.xmlsoap.org/soap/http"/>
  <operation name="GetPrice">
    <soap:operation soapAction="http://example.com/GetPrice"/>
    <input><soap:body use="literal"/></input>
    <output><soap:body use="literal"/></output>
  </operation>
</binding>

<service name="PricingService">
  <port name="PricingPort" binding="tns:PricingSOAP">
    <soap:address location="https://example.com/pricing"/>
  </port>
</service>
<!-- style: rpc or document; use: encoded or literal -->

SOAP 与 REST 对比

SOAP 是带严格 XML 信封、WSDL 契约和 WS-* 标准(安全、事务)的协议;适合需要可靠性和正式契约的企业集成。REST 是轻量级基于 HTTP 的风格,通常用 JSON,主导公共 API。

xml
<!-- SOAP: protocol with envelope, strict contract (WSDL),
     built-in error model (Fault), WS-* extensions.
     Heavier, XML-only, common in enterprise/B2B. -->

<!-- REST: architectural style using HTTP verbs (GET/POST/...),
     flexible payloads (often JSON), no built-in contract.
     Lighter, browser-friendly, dominant on the web. -->

<!-- When to use SOAP:
     - you need strict contracts and tooling
     - transactional reliability (WS-ReliableMessaging)
     - standardized security (WS-Security)
     - existing SOAP-based infrastructure -->

<!-- When to use REST:
     - public APIs, mobile/web clients
     - JSON payloads, HTTP caching
     - simpler, faster iteration -->
17

XML 数字签名

签名结构

XML 签名的根 <Signature> 含 <SignedInfo>(签什么及如何签)、<SignatureValue>(对规范化 SignedInfo 的加密签名)、可选 <KeyInfo> 和持有引用数据的 <Object>。

xml
<?xml version="1.0"?>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
  <SignedInfo>
    <CanonicalizationMethod Algorithm="..."/>
    <SignatureMethod Algorithm="..."/>
    <Reference URI="#data">
      <DigestMethod Algorithm="..."/>
      <DigestValue>...</DigestValue>
    </Reference>
  </SignedInfo>
  <SignatureValue>...</SignatureValue>
  <KeyInfo>...</KeyInfo>
  <Object Id="data">signed content</Object>
</Signature>
<!-- A signature = SignedInfo + SignatureValue + optional KeyInfo -->

SignedInfo

SignedInfo 指定规范化(使字节相同的签名能经受 XML 重新序列化)、签名算法和一个或多个 Reference。每个 Reference 通过 URI 指向数据,列出 Transforms,并给出该数据的 DigestValue。

xml
<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
  <CanonicalizationMethod
      Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
  <SignatureMethod
      Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
  <Reference URI="">
    <Transforms>
      <Transform Algorithm="...enveloped-signature"/>
    </Transforms>
    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <DigestValue>base64digest==</DigestValue>
  </Reference>
</SignedInfo>

Reference 与 Transforms

每个 Reference 标识签什么(URI)、应用 Transforms(规范化、XPath 过滤、移除 enveloped-signature),再用 DigestMethod 哈希。enveloped-signature 转换把 Signature 元素本身排除在它自己的哈希之外。

xml
<Reference URI="#order">
  <Transforms>
    <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
    <XPath xmlns="...">not(ancestor-or-self::ds:Signature)</XPath>
  </Transforms>
  <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
  <DigestValue>abc123base64==</DigestValue>
</Reference>

<!-- Reference types:
     - same-document (URI="#id")
     - external (URI="https://...")
     - detached (URI pointing elsewhere) -->

SignatureValue 与 KeyInfo

SignatureValue 是用签名者私钥对规范化 SignedInfo 签名的 base64 结果。KeyInfo 是可选的,只提示如何找到验证密钥(X.509 证书、RSA 公钥、密钥名或获取 URL)——必须带外验证。

xml
<SignatureValue>base64encodedSignature==</SignatureValue>

<KeyInfo>
  <X509Data>
    <X509SubjectName>CN=Example,O=Example Inc</X509SubjectName>
    <X509Certificate>MIID...base64...==</X509Certificate>
  </X509Data>
</KeyInfo>

<!-- Other KeyInfo options: -->
<!--   <KeyName>key-id</KeyName> -->
<!--   <KeyValue><RSAKeyValue>...</RSAKeyValue></KeyValue> -->
<!--   <RetrievalMethod URI="..."/> -->
<!-- KeyInfo is optional and unverified by itself -->

规范化

规范化把 XML 节点集转为稳定字节,使签名在无关差异(属性顺序、CDATA)下可复现。排他式 c14n(xml-exc-c14n)为签名首选,因为它把命名空间限定在已签子树。

xml
<!-- Two documents that are logically identical may differ in bytes:
     - attribute order
     - namespace declarations
     - CDATA vs escaping
     - whitespace outside elements
     Canonical XML (c14n) produces a canonical byte form. -->

<CanonicalizationMethod
    Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>

<!-- Exclusive c14n (recommended for signatures): -->
<CanonicalizationMethod
    Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>

<!-- Exclusive form avoids leaking inherited namespace declarations
     into the signed digest, preventing wrapping attacks. -->

嵌入式签名

嵌入式(enveloped)签名嵌入它所签文档内部(XML 中最常见)。enveloped-signature 转换在哈希前移除 <Signature> 元素,使签名不属于它自己的摘要。还有分离式和包装式变体。

xml
<?xml version="1.0"?>
<order Id="order">
  <item>Widget</item>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="...exc-c14n#"/>
      <SignatureMethod Algorithm="...rsa-sha256"/>
      <Reference URI="#order">
        <Transforms>
          <Transform Algorithm="...enveloped-signature"/>
        </Transforms>
        <DigestMethod Algorithm="...sha256"/>
        <DigestValue>...</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>...</SignatureValue>
  </Signature>
</order>
<!-- The signature lives INSIDE the signed element -->
19

命名约定

元素命名

选一种风格并一致应用。小写加连字符在 XML 词汇表(HTML、SVG)中常见。名字应足够描述以无歧义但简洁。避免晦涩缩写和过于冗长的名字。

xml
<!-- Good: lowercase, hyphen-separated, descriptive -->
<customer-address>
  <street>123 Main St</street>
  <postal-code>12345</postal-code>
</customer-address>

<!-- Avoid: abbreviations that are unclear -->
<!-- <cust-addr><pc>...</pc></cust-addr> -->

<!-- Avoid: too long -->
<!-- <customerMailingPostalAddressCode> -->

<!-- Be consistent across the whole vocabulary -->

属性命名

属性适合元数据(id、type、href、status)。匹配元素命名风格。布尔值带显式值。不要给属性加元素名前缀(<user> 上的 user-id 冗余)。用 'id' 作唯一标识符。

xml
<product sku="A1" unit-price="9.99" in-stock="true">
  <name>Widget</name>
</product>

<!-- Conventions:
     - attributes are metadata: id, type, href, status
     - use lowercase-hyphen style to match elements
     - boolean attributes still carry a value ("true"/"false")
     - avoid repeating the element name: <user user-id="..."/>
       prefer <user id="..."/> -->

<!-- IDs: use 'id' or '<element>-id' for clarity -->

命名空间 URI 与前缀

命名空间 URI 是标识符,非可获取 URL,但应稳定且自有(用你控制的域名的 HTTPS)。前缀是本地别名——保持短而约定俗成。当 schema 不兼容变更时给 URI 加版本。

xml
<!-- Namespace URIs should be stable, owned, and permanent -->
xmlns:bk  = "https://example.com/ns/books/2024"
xmlns:auth= "https://example.com/ns/auth"

<!-- Prefix conventions:
     - short and mnemonic: xs, xsi, xsl, svg, html, dc
     - lowercase, no punctuation beyond letters
     - 'tns' commonly means 'this namespace' in XSD
     - avoid 'xml' (reserved) -->

<!-- Version the URI when the vocabulary changes incompatibly:
     .../ns/books/2024  vs  .../ns/books/2025 -->

大小写风格

常见 XML 大小写风格:PascalCase(XSD/WSDL、.NET)、camelCase(JSON 衍生、Java)、小写连字符(HTML/SVG、REST 友好)、snake_case(罕见)。关键规则是词汇表内一致——混用风格令人困惑。

xml
<!-- PascalCase (UpperCamelCase): common in XSD/WSDL, .NET -->
<OrderRequest>
  <CustomerId>123</CustomerId>
</OrderRequest>

<!-- camelCase: common in JSON-derived XML, Java ecosystems -->
<orderRequest>
  <customerId>123</customerId>
</orderRequest>

<!-- lowercase-with-hyphens: HTML5, SVG, REST-friendly -->
<order-request>
  <customer-id>123</customer-id>
</order-request>

<!-- snake_case: rare in XML, used in some Python/SQL ecosystems -->
<order_request><customer_id>123</customer_id></order_request>

版本化

显式版本化 XML 词汇表:根元素上的 version 属性最简单;版本化命名空间 URI 最严格(不兼容变更获新 URI)。决定何为次要(增量)vs 主要(破坏性)变更并记录。

xml
<!-- Versioning strategies -->

<!-- 1. Attribute on the root -->
<catalog version="2.0">...</catalog>

<!-- 2. Versioned namespace URI -->
<catalog xmlns="https://example.com/ns/catalog/v2">...</catalog>

<!-- 3. Versioned root element name -->
<catalog-v2>...</catalog-v2>

<!-- Backward-compatible changes: add optional elements/attributes
     Incompatible changes: bump the version (and/or namespace) -->

<!-- Document the policy: which changes are "minor" vs "major" -->

文件与扩展名约定

用公认的扩展名让工具和人识别文件类型。XML 声明的 encoding 属性应与实际字节编码匹配;UTF-8 是安全默认。不建议 UTF-8 XML 加 BOM。

xml
<!-- Common file extensions:
     .xml   generic XML
     .xsd   XML Schema
     .xsl   XSLT stylesheet
     .xslt  XSLT (alternate)
     .dtd   Document Type Definition
     .rss   RSS feed
     .atom  Atom feed
     .svg   SVG graphic
     .wsdl  WSDL service description
     .xhtml XHTML document
     .plist Apple property list -->

<!-- XML files are UTF-8 by default; declare encoding if different:
     <?xml version="1.0" encoding="UTF-8"?> -->
20

最佳实践

良构优先

文档必须先良构才会有工具处理。尽早用解析器验证:xmllint --noout file.xml 报告第一个错误。良构不可商量;对照 schema 的有效性是额外的可选层。

xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <child>properly nested &amp; escaped</child>
</root>

<!-- Always ensure well-formedness before worrying about validity:
     - exactly one root element
     - all tags closed and properly nested
     - attributes quoted and unique per element
     - & < escaped (and > in some contexts)
     - valid encoding declaration -->

验证与 Schema

对照 schema 验证以捕获结构错误。XSD 是带类型、带命名空间词汇表的最常见选择;Schematron 用业务规则补充。xsi:noNamespaceSchemaLocation 把文档链到 XSD;带命名空间用 xsi:schemaLocation。

xml
<!-- Choose a schema language:
     - DTD:      simple, built-in, limited types
     - XSD:      rich types, namespaces, industry standard
     - RelaxNG:  compact syntax, flexible patterns
     - Schematron: rule-based business constraints -->

<?xml version="1.0"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="schema.xsd">
  <item>valid</item>
</root>

<!-- Validate during development; consider it at runtime for
     untrusted input. -->

编码(UTF-8)

UTF-8 是 XML 的默认和推荐编码——它无需实体即可处理所有 Unicode。始终声明编码并以该编码保存文件。避免 BOM;特殊字符也可用数字字符引用。

xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <msg>Café — résumé — 中文 — emoji 🙂</msg>
</root>

<!-- Always declare UTF-8 and save the file as UTF-8.
     Avoid legacy encodings (ISO-8859-1) unless required.
     Do not add a UTF-8 BOM (some parsers mishandle it).
     For non-UTF-8 encodings, declare them accurately. -->

可读性与缩进

一致缩进(2 空格常见)使 XML 可读且 diff 干净。避免过长单行。'xmllint --format' 或 XML 格式化器可规范化空白——但注意 xml:space='preserve' 区域不得重新格式化。

xml
<?xml version="1.0"?>
<catalog>
  <book id="b1">
    <title>XML Guide</title>
    <author>Jane Doe</author>
  </book>
  <book id="b2">
    <title>Advanced XML</title>
    <author>John Smith</author>
  </book>
</catalog>

<!-- Use 2-space indentation; one element per line for mixed
     or deep structures. Tools: xmllint --format, prettier. -->

性能

大文件优先用流式解析器(SAX/StAX)而非 DOM 以降低内存。除非需要,否则禁用 DTD 加载和外部实体。XPath 表达式编译一次复用。内部系统考虑紧凑二进制格式以减小体积和解析时间。

xml
<!-- Parsing large XML efficiently:
     - use SAX/StAX (streaming) instead of DOM for big files
     - disable DTD/external entities if you don't need them
     - validate once, not on every read
     - reuse parser instances (factory creation is costly)
     - consider binary formats (protobuf, CBOR) for internal traffic -->

<!-- Java StAX with XXE disabled:
     factory.setProperty(XMLInputFactory.SUPPORT_DTD, false); -->

<!-- For repeated XPath, compile once and reuse the expression -->

安全(XXE)

XML 外部实体(XXE)攻击滥用 DTD 外部实体读取文件、触发 SSRF 或导致拒绝服务(billion laughs/实体扩展)。最强防御是完全禁用 DTD 和外部实体解析,除非用例需要。

xml
<?xml version="1.0"?>
<!DOCTYPE root [
  <!-- XXE: attacker forces the parser to read a local file -->
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>

<!-- Defenses (do ALL that apply):
     1. Disable DTDs entirely if you don't need them.
     2. Disable external entity resolution.
     3. Disable XInclude.
     4. Validate input; limit entity expansion (billion laughs). -->

<!-- Python: defusedxml
     Java:  XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES = false
     PHP:   libxml_disable_entity_loader(true) -->

这篇内容对您有帮助吗?

学习路径

从零开始学习

通过结构化课程从头学习这个语言。