XSD(XML Schema Definition,XML Schema)是 W3C 的第二代契约语言:自身就是 XML 文档、内建四十余种数据类型、原生支持命名空间,还能自定义类型与继承。上一节列的 DTD 两大缺口——无类型、无命名空间——在这里全部补齐。本节从零写一份带类型与次数约束的完整契约,并跑通校验。
同一个 book 契约,XSD 写出来是这样:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <!-- 契约本身也是 XML:xs 前缀挂上 Schema 命名空间(第4章细讲) --> <xs:element name="inventory"> <xs:complexType> <xs:sequence> <!-- sequence 对应 DTD 的逗号序列:顺序强制 --> <xs:element name="supplier" type="xs:string"/> <xs:element name="book" maxOccurs="unbounded"> <!-- maxOccurs unbounded 对应 DTD 的加号 --> <xs:complexType> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="stock" type="xs:integer"/> <!-- 类型!DTD 做不到的核心能力 --> <xs:element name="price" type="xs:decimal" minOccurs="0"/> <!-- minOccurs 0 对应 DTD 的问号 --> </xs:sequence> <xs:attribute name="sku" type="xs:string" use="required"/> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
对照关系一眼可读:sequence = 逗号;maxOccurs="unbounded" = +;minOccurs="0" = ?;use="required" = #REQUIRED。而 type="xs:integer"、type="xs:decimal" 是 DTD 完全没有的东西——<stock>四十二</stock> 会被当场拦下。
内建类型家族速览:数值 xs:integer、xs:decimal、xs:float;时间 xs:date、xs:dateTime、xs:duration;文本 xs:string、xs:normalizedString、xs:token;标识 xs:ID、xs:IDREF(文档内唯一编号与引用,可部分替代外键);布尔 xs:boolean;还有 xs:anyType 兜底。
真实契约不会只用内建类型。库存在业务上必须是 0 到 10000 的整数,SKU 有固定格式——用 simpleType 加 facet(刻面)收口:
<xs:simpleType name="StockCount"> <xs:restriction base="xs:integer"> <xs:minInclusive value="0"/> <xs:maxInclusive value="10000"/> <!-- 限定整数范围:负库存、天文数字都违规 --> </xs:restriction> </xs:simpleType> <xs:simpleType name="SkuType"> <xs:restriction base="xs:string"> <xs:pattern value="[0-9]{13}"/> <!-- 正则刻面:13 位数字(书号) --> </xs:restriction> </xs:simpleType>
常用 facet:pattern(正则)、minInclusive/maxInclusive(数值范围)、length、minLength/maxLength、enumeration(枚举值)。enumeration 把 DTD 里那个可怜的属性枚举升级成了任意位置的枚举类型。
文档端关联契约:根元素挂 xsi:noNamespaceSchemaLocation(或用 targetNamespace + schemaLocation,涉及命名空间,第 4 章后再回头看):
<?xml version="1.0" encoding="UTF-8"?> <inventory xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="inventory.xsd"> <supplier>华东书源</supplier> <book sku="9787111407010"> <title>算法导论</title> <stock>-5</stock> </book> </inventory>
命令:xmllint --noout --schema inventory.xsd inventory.xml 输出: inventory.xml:6: element stock: Schemas validity error : Element 'stock': '-5' is not a valid value of the atomic type 'StockCount'(若已应用自定义类型) 库存 document fails to validate,退出码非 0
三个观察点:报错指到具体行;给出违规值与违反的类型名;退出码非零让脚本能自动拦截。改回 42 再跑,退出码 0。变式:把 sku 改成 12 位,pattern 刻面拦下;把 price 移到 title 前,sequence 拦下——三种约束各就各位。

⚠️ XSD 的常见坑是"过度约束":把顺序写死成 sequence,对方系统字段顺序一变就违规。数据交换场景若顺序无语义,可用
xs:all或放宽为xs:choice的 maxOccurs 组合,把契约的火力集中在真正要守的条款上。
XSD 里反复出现的 xs: 前缀与 URI,正是命名空间的入口——第 4 章贴标车间开工。