前面学完 DTD,知道 DTD 能管住标签结构和属性,但有个明显的短板:它管不了数据内容本身是什么类型。比如 <age> 标签里写了 "二十" 而不是 20,DTD 照样放行,因为它眼里只有 "这是文本",区分不了数字和字符串。
这时候就该 XML Schema 出场了。
XML Schema(简称 XSD)和 DTD 干的是一类活 —— 给 XML 立规矩。但它比 DTD 强得多,不止管标签结构,还能精确到每个字段是什么数据类型、取值范围多少、能不能为空、最多出现几次。
打个比方:XML 像一份自由填写的表单,没有规范的话,有人把姓名填在年龄栏、有人把日期写成 "明天",乱成一团。DTD 相当于告诉你 "这页表有哪些栏目、谁先谁后",但每个栏里该填什么格式它管不了。XSD 则是升级版规范 —— 不仅告诉你栏目怎么排,还规定 "年龄栏只能填 1 到 150 的整数"、"日期栏必须写 2024-01-01 这种格式",填错一个字都不行。
例如,将前面的 student.dtd 转换为 XSD,保存到 student.xsd 文件中,如下:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="student">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:string"/>
<xs:element name="major" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
</xs:sequence>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>详细说明:
第 1 行:<?xml version="1.0" encoding="UTF-8"?>
XML 标准声明,和普通 XML 文件一样。XSD 文件本身就是一份 XML,所以开头也要写这行。
第 2 行:<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
这是 XSD 文件的根标签,和 XML 必须有唯一根标签一个道理。xmlns:xs 声明了命名空间,xs: 前缀代表后面所有标签都来自 XML Schema 的标准定义。整份 XSD 规则都要写在 <xs:schema> 里面。
第 4 行:<xs:element name="student">
声明一个名为 student 的元素,对应 XML 里的 <student> 根标签。DTD 里是 <!ELEMENT student ...>,XSD 里用 <xs:element> 表达同一个意思。只不过 DTD 一行写完结构和内容,XSD 要嵌套子标签来展开。
第 5 行:<xs:complexType>
这一行表示 student 元素是"复杂类型" —— 也就是说它内部不单是纯文本,还包含子元素和属性。XSD 把元素分成两类:
简单类型(simpleType):标签里面只有文本,不带子标签也没有属性,比如 <name>张三</name>。
复杂类型(complexType):标签里面有子标签,或者身上挂了属性,或者两者都有。
student 既有子标签(name、age 等)又有属性(id),显然是复杂类型。
第 6 行:<xs:sequence>
sequence 的意思是 "按顺序出现"。它规定 student 下面的子元素必须严格按照 name → age → major → email 这个顺序排列。这对应 DTD 里括号中的顺序 (name, age, major, email)—— DTD 用逗号表示先后顺序,XSD 用 <xs:sequence> 表达。
如果不需要强制顺序,可以用 <xs:all> 替代,表示子元素可以按任意顺序出现。如果几者选其一,用 <xs:choice>。
第 7~10 行:子元素声明
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:string"/>
<xs:element name="major" type="xs:string"/>
<xs:element name="email" type="xs:string"/>上面四行分别声明了 name、age、major、email 四个子元素,type="xs:string" 表示内容类型是字符串。这里解释一个关键点:DTD 里写的是 #PCDATA,只能模糊地说 "这是可解析文本",区分不了数字和字符串。XSD 明确指定为 xs:string,而且可以改成 xs:int 让 age 必须是整数 —— 这是 DTD 做不到的。当前翻译为了和原 DTD 保持语义一致,先全部用 xs:string,后面可以按需收紧类型。
第 12 行:<xs:attribute name="id" type="xs:ID" use="required"/>
声明 student 标签带一个 id 属性。拆开看:
name="id":属性名叫 id。
type="xs:ID":属性值类型是 ID,和 DTD 里的 ID 一个意思 —— 值必须唯一,不能重复。
use="required":必填,对应 DTD 里的 #REQUIRED。如果改成 use="optional" 就是可选,对应 #IMPLIED。
XSD 写好后,XML 需要关联它才能生效。在 XML 根标签上加上 xmlns:xsi 和 xsi:noNamespaceSchemaLocation。student_xsd.xml 内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<student id="S001" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="student.xsd">
<name>张三</name>
<age>20</age>
<major>计算机科学</major>
<email>zhangsan@example.com</email>
</student>详细说明:
xmlns:xsi 引入 XML Schema 实例命名空间,这是固定写法。
xsi:noNamespaceSchemaLocation="student.xsd" 指定 XSD 文件路径,这里表示 XSD 文件和 XML 在同一目录下,名叫 student.xsd。解析器读到这行就会自动加载 XSD 并校验。
到这里,打开 VS CODE 将上面两个文件创建在同一个目录下面,如下图:

验证成功,图上没有抛出任何错误信息。
下面通过 5 个方面对两者差异进行比较:
| 对比项 | DTD | XML Schema |
| 数据类型 | 很弱,基本只能区分文本和子标签 | 丰富,int、string、date、boolean 等十几种 |
| 取值范围 | 做不到 | 可以限定最小值、最大值、枚举值 |
| 出现次数 | 只能表示"零或一"、"零或多"、"一或多" | 精确到 minOccurs 和 maxOccurs,比如"最少 1 次最多 5 次" |
| 语法格式 | 非 XML 语法,自成一套 | 本身就是 XML,写法统一 |
| 命名空间 | 不支持 | 原生支持,适合复杂项目 |
简单说,DTD 够用在简单场景,XSD 才是企业级项目的主流选择。
把两者放在一起看就很清楚:
XML 是实例数据,承载实际业务信息,比如一个学生的姓名、年龄、专业。
XSD 是结构模板,给 XML 定框架,规定学生标签下面该有哪些字段、什么类型、什么顺序。
XML 文件通过根标签上的属性关联 XSD 文件,解析器加载 XML 时会自动读取 XSD 并逐项校验,不通过的当场报错。
XSD 里最重要的就这四个东西,搞懂它们基本就能看懂了:
对应 XML 里的标签。XSD 中定义元素的名称、数据类型、是否必填。比如 <name> 标签在 XSD 里就是一个 element,类型是 string。
XSD 里元素分两种:简单元素和复杂元素。区分它们的标准很简单 —— 看这个标签里面能不能再套子标签、身上有没有属性。
标签里只有纯文本,没有子标签,也没有属性,就是简单元素。基本写法:
<xs:element name="标签名" type="数据类型"/>例如:
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:int"/>
<xs:element name="price" type="xs:decimal"/>
<xs:element name="birthday" type="xs:date"/>
<xs:element name="isVip" type="xs:boolean"/>对应的 XML:
<name>张三</name>
<age>20</age>
<price>99.90</price>
<birthday>2004-05-15</birthday>
<isVip>true</isVip>标签里面有子元素、或者身上有属性,就是复杂元素,需要使用 <xs:complexType> 进行定义。
只含子元素(不含属性)
<xs:element name="student">
<xs:complexType>
<xs:sequence>
<!-- 元素 name,类型是字符串 -->
<xs:element name="name" type="xs:string"/>
<!-- 元素 age,类型是数字 -->
<xs:element name="age" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:element>只含属性(不含子元素)
标签里只有文本,但是元素上挂了属性,这也算复杂元素。
<xs:element name="address">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="province" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>对应的 XML 如下:
<address province="四川">成都</address><simpleContent> 表示标签内容是纯文本(简单内容),<extension> 表示在纯文本基础上扩展了属性。这是“有属性的简单元素”的标准写法。
既含子元素又含属性
这就是最常见的复杂元素,你手头的 student 就是这种。例如:
<xs:element name="student">
<xs:complexType>
<!-- 子元素定义 -->
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:int"/>
</xs:sequence>
<!-- 属性定义 -->
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>如果元素中包含有多个子元素,那么如何定义这些子元素的顺序呢?在 <xs:complexType> 里面决定子元素怎么排列,有三个指示器:
<xs:sequence> 按顺序出现
子元素必须严格按照声明顺序排列,一个都不能乱。例如:
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:int"/>
</xs:sequence>对应的 XML:
<!-- 正确:先 name 后 age -->
<name>张三</name>
<age>20</age>
<!-- 错误:顺序反了 -->
<age>20</age>
<name>张三</name><xs:all> 任意顺序,每个最多出现一次
子元素可以随便排,但每个最多出现一次。例如:
<xs:all>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:int"/>
</xs:all>对应的 XML:
<!-- 下面两种都正确 -->
<name>张三</name><age>20</age>
<age>20</age><name>张三</name><xs:choice> 多选一
多个子元素中只能出现其中一个。例如:
<xs:choice>
<xs:element name="phone" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
</xs:choice>对应的 XML:
<!-- 正确:二选一 -->
<phone>13800138000</phone>
<!-- 正确:二选一 -->
<email>test@example.com</email>
<!-- 错误:不能同时出现 -->
<phone>13800138000</phone>
<email>test@example.com</email>XSD 的数据类型体系比 DTD 丰富得太多了。DTD 基本只能区分“这是文本”和“这是子标签”,而 XSD 可以精确到整数、小数、日期、布尔值,还能在类型基础上继续加约束。
XSD 中把数据类型分成两类:
内置类型:XSD 自带的数据类型,拿来就用,比如 xs:string(字符串)、xs:int(整数)、xs:decimal(小数)、xs:date(日期)、xs:boolean(布尔值)等,覆盖了绝大部分日常需求。
自定义类型:在内置类型基础上加约束,或者把多个元素组合成新的复合类型。
下面将逐一介绍内置类型:
最基础的类型,任意字符都行。例如:
<xs:element name="name" type="xs:string"/>对应的 XML:
<name>张三</name>
<name>zhangsan123</name>
<name>Hello World!</name>注意,字符串类型本身不做任何限制,但可以叠加约束,就像这样:
<xs:element name="username">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="3"/>
<xs:maxLength value="20"/>
<xs:pattern value="[a-zA-Z][a-zA-Z0-9_]*"/>
</xs:restriction>
</xs:simpleType>
</xs:element>这里对 username 做了三层限制:最少 3 个字符(minLength)、最多 20 个字符(maxLength)、必须以字母开头后面跟字母数字或下划线(pattern)。
比 xs:string 多一个处理:自动把换行符、制表符替换成空格,适合那种不允许含控制字符的文本。
在 normalizedString 基础上进一步去掉首尾空格,并且把连续多个空格压缩成一个。适合用户名、标签这类不需要多余空格的字段。例如:
<xs:element name="tag" type="xs:token"/>对应 XML 如下:
<!-- 写成这样 -->
<tag> 计算机 科学 </tag>
<!-- 解析后等价于 -->
<tag>计算机 科学</tag>范围 -2,147,483,648 到 2,147,483,647,一般情况够用了。
<xs:element name="age" type="xs:int"/>对应 XML 如下:
<age>20</age>
<age>-1</age>如果不需要负数,可以加约束:
<xs:element name="age">
<xs:simpleType>
<xs:restriction base="xs:int">
<xs:minInclusive value="0"/>
</xs:restriction>
</xs:simpleType>
</xs:element>没有范围限制,适合处理超大数值。
<xs:element name="id" type="xs:integer"/>对应 XML 如下:
<id>99999999999999999999</id>适合金额、价格这种对精度要求高的场景,不会像 float 那样丢精度。
<xs:element name="price" type="xs:decimal"/>对应 XML 如下:
<price>99.90</price>
<price>0.01</price>float 是 32 位,double 是 64 位。科学计算用,日常业务尽量用 decimal,精度更可靠。
<xs:element name="score" type="xs:float"/>
<xs:element name="ratio" type="xs:double"/>格式固定为 YYYY-MM-DD。
<xs:element name="birthday" type="xs:date"/>对应 XML 如下:
<birthday>2004-05-15</birthday>格式固定为 HH:MM:SS,可带时区。
<xs:element name="startTime" type="xs:time"/>对应 XML 如下:
<startTime>14:30:00</startTime>
<startTime>14:30:00+08:00</startTime>格式固定为 YYYY-MM-DDTHH:MM:SS,中间用字母 T 分隔日期和时间。
<xs:element name="createTime" type="xs:dateTime"/>对应 XML 如下:
<createTime>2024-05-15T14:30:00</createTime>分别表示年份、月份、日期片段,用得比较少。
<xs:element name="graduationYear" type="xs:gYear"/>对应 XML 如下:
<graduationYear>2024</graduationYear>只有两个合法值:true 和 false。注意大小写,必须是全小写。
<xs:element name="isVip" type="xs:boolean"/>对应 XML 如下:
<isVip>true</isVip>
<isVip>false</isVip>另外 1 和 0 也分别被当作 true 和 false 接受,但规范推荐写 true/false。
用于存储二进制数据,比如图片、文件的 Base64 或十六进制编码。一般数据交互很少用到,Web Service 场景偶尔出现。
合法的 URI 地址。
<xs:element name="homepage" type="xs:anyURI"/>对应 XML 如下:
<homepage>https://www.example.com</homepage>唯一标识符,和 DTD 里的 ID 一个意思——值在整个 XML 中不能重复,不能以数字开头。
<xs:attribute name="id" type="xs:ID" use="required"/>对应 XML 如下:
<!-- 正确 -->
<student id="S001">...</student>
<student id="S002">...</student>
<!-- 错误,S001 重复了 -->
<student id="S001">...</student>引用其他元素的 ID,用来做跨标签关联。
<xs:attribute name="teacher" type="xs:IDREF"/>
<xs:attribute name="members" type="xs:IDREFS"/>对应 XML 如下:
<student id="S001">...</student>
<student id="S002">...</student>
<!-- teacher 引用 S001 -->
<course teacher="S001">...</course>
<!-- members 引用多个,空格分隔 -->
<team members="S001 S002">...</team>自定义简单类型:基于内置类型加约束
就是把前面的约束写法抽成一个可复用的类型:
<xs:simpleType name="ageType">
<xs:restriction base="xs:int">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="150"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="genderType">
<xs:restriction base="xs:string">
<xs:enumeration value="男"/>
<xs:enumeration value="女"/>
</xs:restriction>
</xs:simpleType>定义好之后,多个元素都能引用同一个类型:
<xs:element name="age" type="ageType"/>
<xs:element name="fatherAge" type="ageType"/>
<xs:element name="gender" type="genderType"/>改约束只需要改类型定义,所有引用自动生效。
自定义复杂类型:组合多个元素
<xs:complexType name="addressType">
<xs:sequence>
<xs:element name="province" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="detail" type="xs:string"/>
</xs:sequence>
</xs:complexType>对应 XML 如下:
<xs:element name="homeAddress" type="addressType"/>
<xs:element name="workAddress" type="addressType"/>一份地址结构,多处复用,不用重复写。
对应 XML 标签上的属性,比如 <student id="S001"> 里的 id。XSD 可以约束属性的类型、是否必填、有没有默认值。
XSD 里属性的写法和元素差不多,都是声明一个名字加一个类型,但属性有一些自己特有的规则需要搞清楚。
基本语法:
<xs:attribute name="属性名" type="数据类型" use="必填规则"/>最简写法,三要素齐全:
<xs:attribute name="id" type="xs:string" use="required"/>name:属性名,对应 XML 里的 id="xxx"
type:属性值的类型
use:必填还是可选
use="required" 必填,不写就报错,最常用。
use="optional" 可选,写不写都行,这也是 use 的默认值。如果你不写 use,解析器就当 optional 处理。
use="prohibited" 禁止出现,这个属性不允许出现在 XML 里,写了就报错。用得不多,一般是在类型继承时用来屏蔽父类型中已有的属性。
使用 default 设置默认值,如果 XML 里没写这个属性时,XML 解析器会自动补上默认值。例如:
<xs:attribute name="status" type="xs:string" default="在读"/>
<xs:attribute name="country" type="xs:string" default="中国"/>对应 XML 如下:
<!-- 显式指定 -->
<student id="S001" status="休学">...</student>
<!-- 没写,解析器自动补 status="在读" country="中国" -->
<student id="S002">...</student>使用 fixed 指定固定值,属性值被锁死了。要么不写(自动用固定值),要么写一模一样的值,改成别的就报错。例如:
<xs:attribute name="version" type="xs:string" fixed="1.0"/>对应 XML 如下:
<!-- 正确:不写 -->
<student id="S001">...</student>
<!-- 解析后等价于 <student id="S001" version="1.0"> -->
<!-- 正确:写了但值一致 -->
<student id="S002" version="1.0">...</student>
<!-- 错误:固定值是 1.0,不能写 2.0 -->
<student id="S003" version="2.0">...</student>注意:一个属性要么设 default 要么设 fixed,不能两个都写。另外用了 default 或 fixed 之后,use 属性就不能设为 required 了 —— 你都已经给默认值了,还要求必填,逻辑上矛盾。
属性的 type 可以用 XSD 的内置类型,但最常用的就这几种:
| 类型 | 说明 | 示例 |
| xs:string | 字符串 | country="中国" |
| xs:int | 整数 | level="5" |
| xs:ID | 唯一标识 | id="S001" |
| xs:IDREF | 引用 ID | teacher="S001" |
| xs:IDREFS | 引用多个 ID | members="S001 S002" |
| xs:boolean | 布尔值 | active="true" |
和元素一样,属性也能加枚举、模式等限制,例如:
<xs:attribute name="gender">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="男"/>
<xs:enumeration value="女"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>对应 XML 如下:
<!-- 正确 -->
<student id="S001" gender="男">...</student>
<!-- 错误 -->
<student id="S002" gender="未知">...</student>属性声明必须放在 <xs:complexType> 里,而且必须在子元素声明之后。顺序是固定的:先声明子元素,再声明属性。如下:
<xs:element name="student">
<xs:complexType>
<!-- 先写子元素 -->
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:int"/>
</xs:sequence>
<!-- 再写属性 -->
<xs:attribute name="id" type="xs:ID" use="required"/>
<xs:attribute name="gender" default="男">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="男"/>
<xs:enumeration value="女"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>标签里只有纯文本,但身上挂了属性,这算复杂元素。写法是 <xs:simpleContent> 套 <xs:extension>:
<xs:element name="address">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="province" type="xs:string" use="required"/>
<xs:attribute name="city" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>对应 XML 如下:
<address province="四川" city="成都">高新区天府大道 100 号</address>注意了: simpleContent 表示这个标签的内容是纯文本,extension base="xs:string" 表示文本是字符串类型,后面再挂两个属性。这个写法看起来绕,但逻辑很清楚:文本是主体,属性是附加在文本上的说明。
如果多个标签都要用同一组属性,可以定义属性组,避免重复声明:
<xs:attributeGroup name="commonAttrs">
<xs:attribute name="id" type="xs:ID" use="required"/>
<xs:attribute name="createTime" type="xs:dateTime"/>
<xs:attribute name="status" type="xs:string" default="正常"/>
</xs:attributeGroup>引用时用 ref 指向属性组名:
<xs:element name="student">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
<xs:attributeGroup ref="commonAttrs"/>
</xs:complexType>
</xs:element>
<xs:element name="teacher">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
<xs:attributeGroup ref="commonAttrs"/>
</xs:complexType>
</xs:element>student 和 teacher 都自动拥有了 id、createTime、status 三个属性,改一处全生效。
XSD 的约束能力是整个规范体系里最值钱的部分。光定义标签和属性只是搭了个骨架,真正让数据不乱套的是下面这些约束规则,这些规则能管到值范围、出现次数、格式模式、甚至值之间的唯一性。
控制一个元素最少出现几次、最多出现几次。默认两个都是 1,也就是必须出现且只能出现一次。例如:
<xs:element name="phone" type="xs:string" minOccurs="0" maxOccurs="3"/>
<xs:element name="hobby" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>详细说明:
minOccurs="0":可以不出现
maxOccurs="3":最多出现 3 次
maxOccurs="unbounded":不设上限,可以出现任意多次
组合效果速查:
| minOccurs | maxOccurs | 含义 |
| 1 | 1 | 必须出现 1 次(默认) |
| 0 | 1 | 可选,最多 1 次 |
| 1 | unbounded | 必须至少出现 1 次,上不封顶 |
| 0 | unbounded | 可选,出现次数不限 |
| 2 | 5 | 至少 2 次,最多 5 次 |
三个指示器决定了子元素之间的排列关系。
子元素必须按声明顺序依次出现,位置不能换。例如:
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:int"/>
<xs:element name="major" type="xs:string"/>
</xs:sequence>子元素可以按任意顺序出现,但每个最多出现一次。minOccurs 只能设为 0 或 1,maxOccurs 只能为 1。例如:
<xs:all>
<xs:element name="name" type="xs:string" minOccurs="1"/>
<xs:element name="age" type="xs:int" minOccurs="0"/> <!-- 可选 -->
<xs:element name="major" type="xs:string" minOccurs="1"/>
</xs:all>注意:all 只能作为 complexType 的直接子标签,不能嵌套在 sequence 或 choice 里面。
多个选项中只能出现其中一个。例如:
<xs:choice>
<xs:element name="phone" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
</xs:choice>三个指示器可以嵌套组合,应对复杂结构:
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="age" type="xs:int"/>
<!-- 联系方式二选一 -->
<xs:choice>
<xs:element name="phone" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
</xs:choice>
<!-- 兴趣爱好,可选,不限次数 -->
<xs:element name="hobby" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>这段约束的意思是:name 和 age 必须按顺序出现,然后 phone 和 email 二选一,最后 hobby 可选、写了可以写很多个。
限制值只能从预定义的几个选项中选。
<xs:element name="gender">
<xs:simpleType>
<!-- gender值为男或女 -->
<xs:restriction base="xs:string">
<xs:enumeration value="男"/>
<xs:enumeration value="女"/>
</xs:restriction>
</xs:simpleType>
</xs:element>枚举也可以用在属性上:
<xs:attribute name="status">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="待支付"/>
<xs:enumeration value="已支付"/>
<xs:enumeration value="已发货"/>
<xs:enumeration value="已完成"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>针对数值类型,可以设上下限:
| 约束 | 含义 |
| minInclusive | 最小值(包含) |
| maxInclusive | 最大值(包含) |
| minExclusive | 最小值(不包含) |
| maxExclusive | 最大值(不包含) |
例如:
<xs:element name="age">
<xs:simpleType>
<xs:restriction base="xs:int">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="150"/>
</xs:restriction>
</xs:simpleType>
</xs:element>如果写 <xs:minExclusive value="0"/>,那 0 本身也不行,必须大于 0。
针对字符串和列表类型:
| 约束 | 含义 |
| length | 精确长度 |
| minLength | 最小长度 |
| maxLength | 最大长度 |
例如:
<xs:element name="username">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="3"/>
<xs:maxLength value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>如果 length 直接定死长度,比如身份证号 18 位:
<xs:element name="idCard">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:length value="18"/>
</xs:restriction>
</xs:simpleType>
</xs:element>totalDigits 控制总位数,fractionDigits 控制小数位数:
<xs:element name="price">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="8"/>
<xs:fractionDigits value="2"/>
</xs:restriction>
</xs:simpleType>
</xs:element>XML 示例:
<!-- 正确,总共 5 位数字,小数 2 位 -->
<price>999.99</price>
<!-- 错误,小数部分超过 2 位 -->
<price>100.999</price>
<!-- 错误,总位数超过 8 -->
<price>9999999.99</price>注意:totalDigits 只算数字,不算小数点。
最灵活的约束方式,用正则表达式精确控制值的格式。例如:
<xs:element name="phone">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="1[3-9]\d{9}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>相关 XML 文档:
<!-- 正确 -->
<phone>13800138000</phone>
<!-- 错误,第二位是 2,不在 3-9 范围 -->
<phone>12000138000</phone>常用 pattern 示例:
<!-- 邮箱 -->
<xs:pattern value="\w+@\w+\.\w+"/>
<!-- 邮政编码,6 位数字 -->
<xs:pattern value="\d{6}"/>
<!-- 车牌号 -->
<xs:pattern value="[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤川青藏琼][A-Z][A-Z0-9]{5}"/>一个元素也可以绑多个 pattern,只要满足其中一个就算通过:
<xs:element name="contact">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="1[3-9]\d{9}"/>
<xs:pattern value="\w+@\w+\.\w+"/>
</xs:restriction>
</xs:simpleType>
</xs:element>手机号或邮箱,满足任意一种格式就行。
控制解析器如何处理值中的空白字符:
| 值 | 效果 |
| preserve | 保留所有空白,不做任何处理(默认) |
| replace | 把换行符、制表符替换成空格 |
| collapse | 在 replace 基础上,去掉首尾空格,把连续空格压缩成一个 |
示例:
<xs:element name="code">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:whiteSpace value="collapse"/>
</xs:restriction>
</xs:simpleType>
</xs:element>对应的 XML:
<!-- 写成这样 -->
<code> ABC 123 </code>
<!-- 解析后变成 "ABC 123" -->这是 DTD 完全没有的能力,保证某个元素的值在整个文档中唯一,但不用 ID 类型(ID 只能管属性)。
<xs:element name="students">
<xs:complexType>
<xs:sequence>
<xs:element name="student" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="email" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<!-- 约束:所有 student 的 email 值必须唯一 -->
<xs:unique name="uniqueEmail">
<xs:selector xpath="student"/>
<xs:field xpath="email"/>
</xs:unique>
</xs:element>详细说明:
selector:用 XPath 选定范围,这里选的是所有 student 元素
field:指定哪个字段要保证唯一,这里是 email
类似于数据库的主键和外键,保证一个元素引用的值在另一个元素中确实存在。
<xs:element name="school">
<xs:complexType>
<xs:sequence>
<!-- 学生列表 -->
<xs:element name="students">
<xs:complexType>
<xs:sequence>
<xs:element name="student" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="id" type="xs:ID" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- 选课记录,引用了学生 id -->
<xs:element name="enrollments">
<xs:complexType>
<xs:sequence>
<xs:element name="enrollment" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="studentId" type="xs:string" use="required"/>
<xs:attribute name="course" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<!-- 定义主键:student 的 id -->
<xs:key name="studentKey">
<xs:selector xpath="students/student"/>
<xs:field xpath="@id"/>
</xs:key>
<!-- 定义外键:enrollment 的 studentId 必须引用已存在的 student id -->
<xs:keyref name="enrollmentRef" refer="studentKey">
<xs:selector xpath="enrollments/enrollment"/>
<xs:field xpath="@studentId"/>
</xs:keyref>
</xs:element>对应的 XML:
<!-- 正确:S001 和 S002 都存在 -->
<school>
<students>
<student id="S001">...</student>
<student id="S002">...</student>
</students>
<enrollments>
<enrollment studentId="S001" course="数学"/>
<enrollment studentId="S002" course="英语"/>
</enrollments>
</school>
<!-- 错误:S999 这个学生不存在 -->
<school>
<students>
<student id="S001">...</student>
</students>
<enrollments>
<enrollment studentId="S999" course="数学"/>
</enrollments>
</school>到这里关于 XSD 的知识就介绍完了,更多知识后续通过专门的教程进行介绍。