引言:理解XML模式语言的重要性

在现代数据交换和配置管理中,XML(可扩展标记语言)扮演着至关重要的角色。为了确保XML文档的结构正确性和数据完整性,我们需要使用模式语言来定义XML文档的规则。XML模式语言主要有两种:DTD(Document Type Definition,文档类型定义)和XSD(XML Schema Definition,XML模式定义)。

DTD是XML早期版本的标准模式语言,语法相对简单但功能有限。XSD是W3C推荐的标准,基于XML语法,功能更加强大,支持数据类型、命名空间等高级特性。随着XML技术的发展,许多遗留系统使用DTD,而现代系统则倾向于使用XSD。因此,掌握DTD到XSD的转换技巧对于系统升级、数据迁移和互操作性具有重要意义。

本文将从基础语法对比开始,逐步深入到实战代码示例,帮助您全面理解DTD和XSD的转换技巧。我们将通过详细的代码示例和步骤说明,让您能够轻松掌握这一重要技能。

DTD基础语法详解

DTD的基本结构

DTD定义了XML文档的元素、属性、实体和注释。它可以内嵌在XML文档中,也可以作为外部文件引用。

内部DTD示例:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE note [ <!ELEMENT note (to, from, heading, body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]> <note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note> 

外部DTD示例(文件名为note.dtd):

<?xml version="1.0" encoding="UTF-8"?> <!ELEMENT note (to, from, heading, body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> 

引用外部DTD的XML文档:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE note SYSTEM "note.dtd"> <note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note> 

DTD元素声明

DTD使用<!ELEMENT>声明元素,语法如下:

<!ELEMENT element-name content-specifier> 

内容说明符(content-specifier)可以是:

  • #PCDATA:解析字符数据(纯文本)
  • 子元素列表:如(to, from, heading, body)
  • 重复符号:*(零次或多次)、+(一次或多次)、?(零次或一次)
  • 选择符号:|(或)
  • 空元素:EMPTY
  • 任意内容:ANY

示例:

<!ELEMENT book (title, author+, chapter*)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT chapter (section*)> <!ELEMENT section (#PCDATA | bold | italic)*> <!ELEMENT bold (#PCDATA)> <!ELEMENT italic (#PCDATA)> 

DTD属性声明

DTD使用<!ATTLIST>声明属性,语法如下:

<!ATTLIST element-name attribute-name attribute-type default-value > 

属性类型包括:

  • CDATA:字符数据
  • (value1|value2|...):枚举值
  • ID:唯一标识符
  • IDREF:引用ID
  • IDREFS:多个ID引用
  • NMTOKEN:名称标记
  • NMTOKENS:多个名称标记
  • ENTITY:实体引用
  • ENTITIES:多个实体引用
  • NOTATION:符号引用

默认值可以是:

  • #REQUIRED:必须提供
  • #IMPLIED:可选
  • #FIXED "value":固定值
  • 直接指定默认值

示例:

<!ATTLIST book id ID #REQUIRED category (fiction|non-fiction|reference) "fiction" lang CDATA "en" isbn CDATA #IMPLIED > 

DTD实体声明

DTD可以声明实体,用于定义可重用的文本或符号。

通用实体:

<!ENTITY copy "©"> <!ENTITY company "ABC Corporation"> 

参数实体(仅在DTD内部使用):

<!ENTITY % common-attrs "id ID #IMPLIED, lang CDATA 'en'"> 

外部实体:

<!ENTITY external SYSTEM "external.dtd"> 

DTD的局限性

尽管DTD简单易用,但它存在以下局限性:

  1. 数据类型支持有限:只能定义文本内容,无法指定具体数据类型(如整数、日期等)
  2. 不支持命名空间:无法处理XML命名空间
  3. 语法不基于XML:DTD有自己的语法,与XML语法不同
  4. 扩展性差:缺乏面向对象特性,如继承和派生

XSD基础语法详解

XSD的基本结构

XSD本身是XML文档,因此遵循XML语法规则。根元素是<xs:schema>

基本XSD示例:

<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="note"> <xs:complexType> <xs:sequence> <xs:element name="to" type="xs:string"/> <xs:element name="from" type="xs:string"/> <xs:element name="heading" type="xs:string"/> <xs:element name="body" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema> 

XSD元素声明

XSD使用<xs:element>声明元素,可以是全局元素或局部元素。

全局元素(在schema根下定义):

<xs:element name="book" type="bookType"/> 

局部元素(在complexType内部定义):

<xs:complexType name="bookType"> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string" maxOccurs="unbounded"/> <xs:element name="chapter" type="chapterType" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> <xs:attribute name="id" type="xs:ID" use="required"/> <xs:attribute name="category" type="xs:string" default="fiction"/> </xs:complexType> 

XSD数据类型

XSD提供了丰富的数据类型系统:

内置基本数据类型:

  • xs:string:字符串
  • xs:integer:整数
  • xs:decimal:十进制数
  • xs:date:日期
  • xs:dateTime:日期时间
  • xs:boolean:布尔值
  • xs:ID, xs:IDREF:用于唯一标识和引用

派生数据类型:

<xs:simpleType name="positiveInteger"> <xs:restriction base="xs:integer"> <xs:minInclusive value="1"/> </xs:restriction> </xs:simpleType> 

枚举类型:

<xs:simpleType name="categoryType"> <xs:restriction base="xs:string"> <xs:enumeration value="fiction"/> <xs:enumeration value="non-fiction"/> <xs:enumeration value="reference"/> </xs:restriction> </xs:simpleType> 

XSD复杂类型

复杂类型定义包含子元素或属性的元素。

定义复杂类型:

<xs:complexType name="addressType"> <xs:sequence> <xs:element name="street" type="xs:string"/> <xs:element name="city" type="xs:string"/> <xs:element name="state" type="xs:string"/> <xs:element name="zip" type="xs:string"/> </xs:sequence> <xs:attribute name="type" type="xs:string" use="optional"/> </xs:complexType> 

匿名复杂类型:

<xs:element name="person"> <xs:complexType> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="age" type="xs:positiveInteger"/> </xs:sequence> </xs:complexType> </xs:element> 

XSD命名空间支持

XSD原生支持XML命名空间,这是其重要优势。

使用命名空间的XSD:

<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.com/book" xmlns:bk="http://example.com/book" elementFormDefault="qualified"> <xs:element name="book" type="bk:bookType"/> <xs:complexType name="bookType"> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:schema> 

DTD与XSD语法对比

元素定义对比

DTD:

<!ELEMENT book (title, author+, chapter*)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT chapter (section*)> <!ELEMENT section (#PCDATA)> 

XSD:

<xs:element name="book"> <xs:complexType> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string" maxOccurs="unbounded"/> <xs:element name="chapter" type="chapterType" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="chapterType"> <xs:sequence> <xs:element name="section" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> 

属性定义对比

DTD:

<!ATTLIST book id ID #REQUIRED category (fiction|non-fiction|reference) "fiction" lang CDATA "en" isbn CDATA #IMPLIED > 

XSD:

<xs:element name="book"> <xs:complexType> <xs:sequence> <!-- 子元素定义 --> </xs:sequence> <xs:attribute name="id" type="xs:ID" use="required"/> <xs:attribute name="category" type="categoryType" default="fiction"/> <xs:attribute name="lang" type="xs:string" default="en"/> <xs:attribute name="isbn" type="xs:string" use="optional"/> </xs:complexType> </xs:element> <xs:simpleType name="categoryType"> <xs:restriction base="xs:string"> <xs:enumeration value="fiction"/> <xs:enumeration value="non-fiction"/> <icenumeration value="reference"/> </xs:restriction> </xs:simpleType> 

数据类型支持对比

DTD: 只能使用#PCDATA表示文本数据,无法指定具体类型。

XSD: 支持丰富的数据类型系统:

<xs:element name="age" type="xs:positiveInteger"/> <xs:element name="price" type="xs:decimal"/> <xs:element name="published" type="xs:date"/> <xs:element name="available" type="xs:boolean"/> 

命名空间支持对比

DTD: 不支持命名空间,无法处理带前缀的元素。

XSD: 原生支持命名空间:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.com/ns1" xmlns:ns1="http://example.com/ns1"> <xs:import namespace="http://example.com/ns2"/> <xs:element name="root" type="ns1:rootType"/> </xs:schema> 

DTD到XSD转换规则

转换基本原则

  1. 保持语义等价:转换后的XSD必须能够验证原始DTD所能验证的所有有效XML文档
  2. 利用XSD增强功能:在保持兼容性的前提下,可以利用XSD的增强功能
  3. 结构映射:将DTD的元素、属性、实体等结构映射到XSD的对应结构
  4. 数据类型强化:尽可能使用XSD的强数据类型

元素转换规则

DTD:

<!ELEMENT element-name content-specifier> 

XSD映射:

  • #PCDATAtype="xs:string"
  • (child1, child2)<xs:sequence>
  • *minOccurs="0" maxOccurs="unbounded"
  • +minOccurs="1" maxOccurs="unbounded"
  • ?minOccurs="0" maxOccurs="1"
  • |<xs:choice>

示例:

<!ELEMENT book (title, author+, chapter*)> 

转换为XSD:

<xs:element name="book"> <xs:complexType> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string" maxOccurs="unbounded"/> <xs:element name="chapter" type="chapterType" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> 

属性转换规则

DTD:

<!ATTLIST element-name attr-name attr-type default-value > 

XSD映射:

  • CDATAtype="xs:string"
  • (v1|v2|...)<xs:simpleType> with <xs:enumeration>
  • IDtype="xs:ID"
  • IDREFtype="xs:IDREF"
  • #REQUIREDuse="required"
  • #IMPLIEDuse="optional"
  • #FIXED "value"use="fixed" value="value"
  • 默认值 → default="value"

示例:

<!ATTLIST book id ID #REQUIRED category (fiction|non-fiction|reference) "fiction" lang CDATA "en" > 

转换为XSD:

<xs:element name="book"> <xs:complexType> <xs:attribute name="id" type="xs:ID" use="required"/> <xs:attribute name="category" type="categoryType" default="fiction"/> <xs:attribute name="lang" type="xs:string" default="en"/> </xs:complexType> </xs:element> <xs:simpleType name="categoryType"> <xs:restriction base="xs:string"> <xs:enumeration value="fiction"/> <xs:enumeration value="non-fiction"/> <xs:enumeration value="reference"/> </xs:restriction> </xs:simpleType> 

实体转换规则

DTD实体:

<!ENTITY copy "©"> <!ENTITY company "ABC Corporation"> 

XSD处理方式: XSD不直接支持实体声明,但可以通过以下方式处理:

  1. 在XML文档中使用实体:在XML中声明实体,XSD验证时会自动处理
  2. 使用固定属性值:如果实体用于默认值,可以使用固定属性
  3. 在文档中直接替换:将实体引用替换为实际值

示例:

<!-- DTD --> <!ENTITY company "ABC Corporation"> <!ELEMENT company (#PCDATA)> <!-- XML使用 --> <company>&company;</company> 

XSD转换:

<!-- XSD无法直接表示实体,但可以验证包含实体的XML --> <xs:element name="company" type="xs:string"/> <!-- 或者,如果company是固定值,可以使用固定属性 --> <xs:element name="company"> <xs:complexType> <xs:attribute name="name" type="xs:string" use="fixed" value="ABC Corporation"/> </xs:complexType> </xs:element> 

复杂结构转换

DTD中的嵌套结构:

<!ELEMENT book (title, author+, (chapter | appendix)*, references?)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT chapter (section*)> <!ELEMENT appendix (section*)> <!ELEMENT section (#PCDATA | bold | italic)*> <!ELEMENT bold (#PCDATA)> <!ELEMENT italic (#PCDATA)> <!ELEMENT references (reference+)> <!ELEMENT reference (#PCDATA)> 

转换为XSD:

<xs:element name="book"> <xs:complexType> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string" maxOccurs="unbounded"/> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="chapter" type="chapterType"/> <xs:element name="appendix" type="appendixType"/> </xs:choice> <xs:element name="references" type="referencesType" minOccurs="0" maxOccurs="1"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="chapterType"> <xs:sequence> <xs:element name="section" type="sectionType" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> <xs:complexType name="appendixType"> <xs:sequence> <xs:element name="section" type="sectionType" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> <xs:complexType name="sectionType"> <xs:complexContent> <xs:extension base="xs:string"> <xs:sequence> <xs:element name="bold" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> <xs:element name="italic" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="referencesType"> <xs:sequence> <xs:element name="reference" type="xs:string" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> 

实战代码示例:完整转换案例

案例1:简单图书目录转换

原始DTD(bookstore.dtd):

<?xml version="1.0" encoding="UTF-8"?> <!ELEMENT bookstore (book+)> <!ELEMENT book (title, author, price, publish_date)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ELEMENT price (#PCDATA)> <!ELEMENT publish_date (#PCDATA)> <!ATTLIST book id ID #REQUIRED category (fiction|non-fiction|technical|children) "fiction" isbn CDATA #IMPLIED > 

转换为XSD(bookstore.xsd):

<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.com/bookstore" xmlns:bk="http://example.com/bookstore" elementFormDefault="qualified"> <!-- 根元素定义 --> <xs:element name="bookstore" type="bk:bookstoreType"/> <!-- bookstore复杂类型 --> <xs:complexType name="bookstoreType"> <xs:sequence> <xs:element name="book" type="bk:bookType" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> <!-- book复杂类型 --> <xs:complexType name="bookType"> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="xs:string"/> <xs:element name="price" type="xs:decimal"/> <xs:element name="publish_date" type="xs:date"/> </xs:sequence> <xs:attribute name="id" type="xs:ID" use="required"/> <xs:attribute name="category" type="bk:categoryType" default="fiction"/> <xs:attribute name="isbn" type="xs:string" use="optional"/> </xs:complexType> <!-- 枚举类型定义 --> <xs:simpleType name="categoryType"> <xs:restriction base="xs:string"> <xs:enumeration value="fiction"/> <xs:enumeration value="non-fiction"/> <xs:enumeration value="technical"/> <xs:enumeration value="children"/> </xs:restriction> </xs:simpleType> </xs:schema> 

验证XML示例(bookstore.xml):

<?xml version="1.0" encoding="UTF-8"?> <bookstore xmlns="http://example.com/bookstore" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://example.com/bookstore bookstore.xsd"> <book id="b1" category="technical" isbn="978-0123456789"> <title>XML Mastery</title> <author>Jane Doe</author> <price>49.99</price> <publish_date>2023-01-15</publish_date> </book> <book id="b2" category="fiction"> <title>The XML Chronicles</title> <author>John Smith</author> <price>19.99</price> <publish_date>2023-03-22</publish_date> </book> </bookstore> 

案例2:复杂订单系统转换

原始DTD(order.dtd):

<!ELEMENT order (customer, item+, payment, shipping)> <!ELEMENT customer (name, address, email)> <!ELEMENT name (#PCDATA)> <!ELEMENT address (street, city, state, zip)> <!ELEMENT street (#PCDATA)> <!ELEMENT city (#PCDATA)> <!ELEMENT state (#PCDATA)> <!ELEMENT zip (#PCDATA)> <!ELEMENT email (#PCDATA)> <!ELEMENT item (product, quantity, unit_price)> <!ELEMENT product (#PCDATA)> <!ELEMENT quantity (#PCDATA)> <!ELEMENT unit_price (#PCDATA)> <!ELEMENT payment (method, details)> <!ELEMENT method (credit_card | paypal | bank_transfer)> <!ELEMENT credit_card (number, expiry, cvv)> <!ELEMENT number (#PCDATA)> <!ELEMENT expiry (#PCDATA)> <!ELEMENT cvv (#PCDATA)> <!ELEMENT paypal (email, password)> <!ELEMENT password (#PCDATA)> <!ELEMENT bank_transfer (account, routing)> <!ELEMENT account (#PCDATA)> <!ELEMENT routing (#PCDATA)> <!ELEMENT details (#PCDATA)> <!ELEMENT shipping (address, method, cost)> <!ATTLIST order id ID #REQUIRED date CDATA #REQUIRED status (pending|processing|shipped|delivered|cancelled) "pending" > <!ATTLIST item sku CDATA #REQUIRED discount CDATA "0.00" > <!ATTLIST shipping method (standard|express|overnight) "standard" > 

转换为XSD(order.xsd):

<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.com/order" xmlns:ord="http://example.com/order" elementFormDefault="qualified"> <!-- 根元素 --> <xs:element name="order" type="ord:orderType"/> <!-- 订单类型 --> <xs:complexType name="orderType"> <xs:sequence> <xs:element name="customer" type="ord:customerType"/> <xs:element name="item" type="ord:itemType" maxOccurs="unbounded"/> <xs:element name="payment" type="ord:paymentType"/> <xs:element name="shipping" type="ord:shippingType"/> </xs:sequence> <xs:attribute name="id" type="xs:ID" use="required"/> <xs:attribute name="date" type="xs:date" use="required"/> <xs:attribute name="status" type="ord:statusType" default="pending"/> </xs:complexType> <!-- 客户类型 --> <xs:complexType name="customerType"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="address" type="ord:addressType"/> <xs:element name="email" type="xs:string"/> </xs:sequence> </xs:complexType> <!-- 地址类型 --> <xs:complexType name="addressType"> <xs:sequence> <xs:element name="street" type="xs:string"/> <xs:element name="city" type="xs:string"/> <xs:element name="state" type="xs:string"/> <xs:element name="zip" type="xs:string"/> </xs:sequence> </xs:complexType> <!-- 商品项类型 --> <xs:complexType name="itemType"> <xs:sequence> <xs:element name="product" type="xs:string"/> <xs:element name="quantity" type="xs:positiveInteger"/> <xs:element name="unit_price" type="xs:decimal"/> </xs:sequence> <xs:attribute name="sku" type="xs:string" use="required"/> <xs:attribute name="discount" type="xs:decimal" default="0.00"/> </xs:complexType> <!-- 支付类型 --> <xs:complexType name="paymentType"> <xs:sequence> <xs:choice> <xs:element name="credit_card" type="ord:creditCardType"/> <xs:element name="paypal" type="ord:paypalType"/> <xs:element name="bank_transfer" type="ord:bankTransferType"/> </xs:choice> <xs:element name="details" type="xs:string"/> </xs:sequence> </xs:complexType> <!-- 信用卡类型 --> <xs:complexType name="creditCardType"> <xs:sequence> <xs:element name="number" type="xs:string"/> <xs:element name="expiry" type="xs:string"/> <xs:element name="cvv" type="xs:string"/> </xs:sequence> </xs:complexType> <!-- PayPal类型 --> <xs:complexType name="paypalType"> <xs:sequence> <xs:element name="email" type="xs:string"/> <xs:element name="password" type="xs:string"/> </xs:sequence> </xs:complexType> <!-- 银行转账类型 --> <xs:complexType name="bankTransferType"> <xs:sequence> <xs:element name="account" type="xs:string"/> <xs:element name="routing" type="xs:string"/> </xs:sequence> </xs:complexType> <!-- 配送类型 --> <xs:complexType name="shippingType"> <xs:sequence> <xs:element name="address" type="ord:addressType"/> <xs:element name="method" type="ord:shippingMethodType"/> <xs:element name="cost" type="xs:decimal"/> </xs:sequence> <xs:attribute name="method" type="ord:shippingMethodType" default="standard"/> </xs:complexType> <!-- 状态枚举 --> <xs:simpleType name="statusType"> <xs:restriction base="xs:string"> <xs:enumeration value="pending"/> <xs:enumeration value="processing"/> <xs:enumeration value="shipped"/> <xs:enumeration value="delivered"/> <xs:enumeration value="cancelled"/> </xs:restriction> </xs:simpleType> <!-- 配送方法枚举 --> <xs:simpleType name="shippingMethodType"> <xs:restriction base="xs:string"> <xs:enumeration value="standard"/> <xs:enumeration value="express"/> <xs:enumeration value="overnight"/> </xs:restriction> </xs:simpleType> </xs:schema> 

验证XML示例(order.xml):

<?xml version="1.0" encoding="UTF-8"?> <order xmlns="http://example.com/order" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://example.com/order order.xsd" id="ord-2023-001" date="2023-10-15" status="processing"> <customer> <name>John Doe</name> <address> <street>123 Main St</street> <city>Anytown</city> <state>CA</state> <zip>12345</zip> </address> <email>john.doe@example.com</email> </customer> <item sku="XML-BOOK-001" discount="0.10"> <product>XML Mastery Guide</product> <quantity>2</quantity> <unit_price>49.99</unit_price> </item> <item sku="XML-TOOL-002"> <product>XML Editor Pro</product> <quantity>1</quantity> <unit_price>199.99</unit_price> </item> <payment> <credit_card> <number>4111111111111111</number> <expiry>12/25</expiry> <cvv>123</cvv> </credit_card> <details>Credit Card Payment</details> </payment> <shipping method="express"> <address> <street>123 Main St</street> <city>Anytown</city> <state>CA</state> <zip>12345</zip> </address> <method>express</method> <cost>15.99</cost> </shipping> </order> 

案例3:带命名空间的复杂转换

原始DTD(config.dtd):

<!ELEMENT configuration (database?, logging?, caching?, plugins?)> <!ELEMENT database (host, port, name, user, password)> <!ELEMENT host (#PCDATA)> <!ELEMENT port (#PCDATA)> <!ELEMENT name (#PCDATA)> <!ELEMENT user (#PCDATA)> <!ELEMENT password (#PCDATA)> <!ELEMENT logging (level, file?)> <!ELEMENT level (debug|info|warning|error|critical)> <!ELEMENT file (#PCDATA)> <!ELEMENT caching (enabled, ttl, max_size)> <!ELEMENT enabled (#PCDATA)> <!ELEMENT ttl (#PCDATA)> <!ELEMENT max_size (#PCDATA)> <!ELEMENT plugins (plugin*)> <!ELEMENT plugin (name, config?)> <!ELEMENT config (property*)> <!ELEMENT property (name, value)> <!ATTLIST configuration version CDATA #REQUIRED environment (development|staging|production) "development" > <!ATTLIST plugin enabled (true|false) "true" priority CDATA "0" > 

转换为XSD(config.xsd):

<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.com/config" xmlns:cfg="http://example.com/config" elementFormDefault="qualified"> <!-- 根元素 --> <xs:element name="configuration" type="cfg:configType"/> <!-- 配置类型 --> <xs:complexType name="configType"> <xs:sequence> <xs:element name="database" type="cfg:databaseType" minOccurs="0"/> <xs:element name="logging" type="cfg:loggingType" minOccurs="0"/> <xs:element name="caching" type="cfg:cachingType" minOccurs="0"/> <xs:element name="plugins" type="cfg:pluginsType" minOccurs="0"/> </xs:sequence> <xs:attribute name="version" type="xs:string" use="required"/> <xs:attribute name="environment" type="cfg:envType" default="development"/> </xs:complexType> <!-- 数据库类型 --> <xs:complexType name="databaseType"> <xs:sequence> <xs:element name="host" type="xs:string"/> <xs:element name="port" type="xs:positiveInteger"/> <xs:element name="name" type="xs:string"/> <xs:element name="user" type="xs:string"/> <xs:element name="password" type="xs:string"/> </xs:sequence> </xs:complexType> <!-- 日志类型 --> <xs:complexType name="loggingType"> <xs:sequence> <xs:element name="level" type="cfg:logLevelType"/> <xs:element name="file" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> <!-- 缓存类型 --> <xs:complexType name="cachingType"> <xs:sequence> <xs:element name="enabled" type="xs:boolean"/> <xs:element name="ttl" type="xs:positiveInteger"/> <xs:element name="max_size" type="xs:positiveInteger"/> </xs:sequence> </xs:complexType> <!-- 插件类型 --> <xs:complexType name="pluginsType"> <xs:sequence> <xs:element name="plugin" type="cfg:pluginType" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> <!-- 单个插件类型 --> <xs:complexType name="pluginType"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="config" type="cfg:configSectionType" minOccurs="0"/> </xs:sequence> <xs:attribute name="enabled" type="xs:boolean" default="true"/> <xs:attribute name="priority" type="xs:integer" default="0"/> </xs:complexType> <!-- 配置段类型 --> <xs:complexType name="configSectionType"> <xs:sequence> <xs:element name="property" type="cfg:propertyType" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> <!-- 属性类型 --> <xs:complexType name="propertyType"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="value" type="xs:string"/> </xs:sequence> </xs:complexType> <!-- 日志级别枚举 --> <xs:simpleType name="logLevelType"> <xs:restriction base="xs:string"> <xs:enumeration value="debug"/> <xs:enumeration value="info"/> <xs:enumeration value="warning"/> <xs:enumeration value="error"/> <xs:enumeration value="critical"/> </xs:restriction> </xs:simpleType> <!-- 环境枚举 --> <xs:simpleType name="envType"> <xs:restriction base="xs:string"> <xs:enumeration value="development"/> <xs:enumeration value="staging"/> <xs:enumeration value="production"/> </xs:restriction> </xs:simpleType> </xs:schema> 

验证XML示例(config.xml):

<?xml version="1.0" encoding="UTF-8"?> <configuration xmlns="http://example.com/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://example.com/config config.xsd" version="1.0" environment="production"> <database> <host>db.example.com</host> <port>5432</port> <name>appdb</name> <user>appuser</user> <password>secret123</password> </database> <logging> <level>info</level> <file>/var/log/app.log</file> </logging> <caching> <enabled>true</enabled> <ttl>3600</ttl> <max_size>1000</max_size> </caching> <plugins> <plugin enabled="true" priority="10"> <name>auth</name> <config> <property> <name>session_timeout</name> <value>3600</value> </property> <property> <name>max_attempts</name> <value>5</value> </property> </config> </plugin> <plugin enabled="true" priority="5"> <name>cache</name> <config> <property> <name>backend</name> <value>redis</value> </property> </config> </plugin> </plugins> </configuration> 

自动化转换工具和方法

使用XSLT进行转换

XSLT(可扩展样式表语言转换)是一种强大的XML转换语言,可以用于将DTD转换为XSD。

XSLT转换模板示例:

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xsl:output method="xml" indent="yes"/> <!-- 根模板 --> <xsl:template match="/"> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xsl:apply-templates select="document('input.dtd')//ELEMENT"/> </xs:schema> </xsl:template> <!-- 元素模板 --> <xsl:template match="ELEMENT"> <xs:element name="{@name}"> <xsl:choose> <xsl:when test="contains(., '#PCDATA')"> <xs:simpleType> <xs:restriction base="xs:string"/> </xs:simpleType> </xsl:when> <xsl:otherwise> <xs:complexType> <xsl:call-template name="process-content"> <xsl:with-param name="content" select="."/> </xsl:call-template> </xs:complexType> </xsl:otherwise> </xsl:choose> </xs:element> </xsl:template> <!-- 处理内容模型 --> <xsl:template name="process-content"> <xsl:param name="content"/> <xsl:choose> <xsl:when test="contains($content, ',')"> <xs:sequence> <xsl:call-template name="split-elements"> <xsl:with-param name="list" select="$content"/> </xsl:call-template> </xs:sequence> </xsl:when> <xsl:when test="contains($content, '|')"> <xs:choice> <xsl:call-template name="split-elements"> <xsl:with-param name="list" select="$content"/> </xsl:call-template> </xs:choice> </xsl:when> <xsl:otherwise> <xs:element name="{$content}"/> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- 分割元素列表 --> <xsl:template name="split-elements"> <xsl:param name="list"/> <xsl:choose> <xsl:when test="contains($list, ',')"> <xsl:variable name="first" select="substring-before($list, ',')"/> <xsl:variable name="rest" select="substring-after($list, ',')"/> <xs:element name="{$first}"/> <xsl:call-template name="split-elements"> <xsl:with-param name="list" select="$rest"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xs:element name="{$list}"/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet> 

使用方法:

# 使用XSLT处理器(如Saxon或xsltproc)进行转换 xsltproc dtd-to-xsd.xsl input.dtd > output.xsd 

使用在线转换工具

  1. XMLGrid.net DTD to XSD Converter:提供在线转换服务,支持上传DTD文件并生成XSD
  2. Trang:命令行工具,支持多种格式转换(DTD/XSD/RelaxNG)
     java -jar trang.jar -I dtd -O xsd input.dtd output.xsd 
  3. Oxygen XML Editor:商业XML编辑器,内置DTD到XSD转换功能

手动转换最佳实践

  1. 逐步转换:先转换基本结构,再添加数据类型和约束
  2. 验证测试:转换后使用验证器测试XML文档的有效性
  3. 保留注释:在XSD中添加注释说明原始DTD结构
  4. 使用命名空间:为XSD添加适当的命名空间以提高可重用性
  5. 文档化:记录转换规则和特殊处理

常见问题与解决方案

问题1:DTD实体处理

问题描述:DTD中的实体在XSD中没有直接对应。

解决方案

  • 在XML文档中保留实体声明
  • 使用XSD的固定属性值
  • 在应用层处理实体替换

示例:

<!-- XML文档中保留实体 --> <!DOCTYPE configuration [ <!ENTITY prod_server "prod.example.com"> ]> <configuration> <host>&prod_server;</host> </configuration> <!-- XSD验证时,实体会被自动解析 --> <xs:element name="host" type="xs:string"/> 

问题2:混合内容处理

问题描述:DTD中的混合内容(#PCDATA与元素混合)在XSD中需要特殊处理。

解决方案:使用<xs:complexType><xs:simpleContent>结合。

示例:

<!-- DTD --> <!ELEMENT para (#PCDATA | bold | italic)*> <!ELEMENT bold (#PCDATA)> <!ELEMENT italic (#PCDATA)> 

XSD转换:

<xs:element name="para"> <xs:complexType mixed="true"> <xs:sequence> <xs:element name="bold" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> <xs:element name="italic" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> 

问题3:ID/IDREF约束

问题描述:DTD中的ID/IDREF约束在XSD中需要保持一致。

解决方案:XSD原生支持ID/IDREF,直接映射即可。

示例:

<!-- DTD --> <!ELEMENT book (title, author*)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> <!ATTLIST book id ID #REQUIRED> <!ATTLIST author ref IDREF #IMPLIED> 

XSD转换:

<xs:element name="book"> <xs:complexType> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="author" type="authorType" maxOccurs="unbounded"/> </xs:sequence> <xs:attribute name="id" type="xs:ID" use="required"/> </xs:complexType> </xs:element> <xs:complexType name="authorType"> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute name="ref" type="xs:IDREF" use="optional"/> </xs:extension> </xs:simpleContent> </xs:complexType> 

问题4:默认值和固定值

问题描述:DTD中的#FIXED和默认值在XSD中的处理。

解决方案:XSD使用defaultfixed属性。

示例:

<!-- DTD --> <!ATTLIST config mode CDATA #FIXED "production" debug (true|false) "false" > 

XSD转换:

<xs:attribute name="mode" type="xs:string" use="fixed" value="production"/> <xs:attribute name="debug" type="xs:boolean" default="false"/> 

问题5:条件内容模型

问题描述:DTD中的条件内容模型(如|,组合)在XSD中的复杂映射。

解决方案:使用<xs:choice><xs:sequence><xs:all>组合。

示例:

<!-- DTD --> <!ELEMENT order (customer, (item | gift)*, payment, shipping?)> 

XSD转换:

<xs:element name="order"> <xs:complexType> <xs:sequence> <xs:element name="customer" type="customerType"/> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="item" type="itemType"/> <xs:element name="gift" type="giftType"/> </xs:choice> <xs:element name="payment" type="paymentType"/> <xs:element name="shipping" type="shippingType" minOccurs="0"/> </xs:sequence> </xs:complexType> </xs:element> 

验证和测试转换结果

使用验证器验证XSD

Java代码示例(使用javax.xml.validation):

import javax.xml.XMLConstants; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import java.io.File; public class XSDValidator { public static void main(String[] args) { try { // 创建SchemaFactory SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); // 加载XSD文件 Source schemaSource = new StreamSource(new File("bookstore.xsd")); Schema schema = factory.newSchema(schemaSource); // 创建Validator Validator validator = schema.newValidator(); // 验证XML文件 Source xmlSource = new StreamSource(new File("bookstore.xml")); validator.validate(xmlSource); System.out.println("XML验证成功!"); } catch (Exception e) { System.err.println("XML验证失败:" + e.getMessage()); e.printStackTrace(); } } } 

Python代码示例(使用lxml库):

from lxml import etree def validate_xml_with_xsd(xsd_file, xml_file): try: # 加载XSD with open(xsd_file, 'rb') as f: xsd_doc = etree.parse(f) xsd_schema = etree.XMLSchema(xsd_doc) # 加载XML with open(xml_file, 'rb') as f: xml_doc = etree.parse(f) # 验证 if xsd_schema.validate(xml_doc): print("XML验证成功!") return True else: print("XML验证失败:") for error in xsd_schema.error_log: print(f" 行 {error.line}: {error.message}") return False except Exception as e: print(f"验证过程中出错:{e}") return False # 使用示例 validate_xml_with_xsd('bookstore.xsd', 'bookstore.xml') 

转换前后验证对比

步骤:

  1. 使用原始DTD验证原始XML
  2. 转换XSD
  3. 使用XSD验证原始XML
  4. 确保验证结果一致

验证脚本示例:

#!/bin/bash # 使用xmllint验证DTD echo "使用DTD验证..." xmllint --valid --noout original.xml # 使用xmllint验证XSD echo "使用XSD验证..." xmllint --schema converted.xsd original.xml --noout # 比较结果 if [ $? -eq 0 ]; then echo "验证成功:XSD转换正确" else echo "验证失败:需要检查转换" fi 

高级技巧和最佳实践

1. 使用XSD 1.1特性

XSD 1.1引入了断言和条件类型定义,可以更好地模拟DTD的灵活性。

示例:使用断言验证业务规则

<xs:element name="order"> <xs:complexType> <xs:sequence> <xs:element name="item" type="itemType" maxOccurs="unbounded"/> </xs:sequence> <xs:assert test="sum(item/price * item/quantity) le 10000"/> </xs:complexType> </xs:element> 

2. 模式派生和重用

利用XSD的类型派生机制创建可重用的组件。

示例:

<!-- 基础地址类型 --> <xs:complexType name="baseAddressType"> <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:complexType> <!-- 派生的美国地址类型 --> <xs:complexType name="usAddressType"> <xs:complexContent> <xs:extension base="baseAddressType"> <xs:sequence> <xs:element name="state" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> 

3. 使用替代组

使用<xs:alternative>实现条件类型定义。

<xs:element name="payment"> <xs:alternative test="@type='credit'" type="creditCardType"/> <xs:alternative test="@type='paypal'" type="paypalType"/> <xs:alternative type="defaultPaymentType"/> </xs:element> 

4. 版本控制和文档化

在XSD中添加注释和版本信息。

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.com/bookstore" xmlns:bk="http://example.com/bookstore" elementFormDefault="qualified"> <!-- 版本: 2.0 日期: 2023-10-15 说明: 从DTD v1.0转换而来,增加了数据类型和命名空间支持 变更: - 添加了ISBN格式验证 - 价格改为decimal类型 - 日期改为date类型 --> <xs:element name="bookstore" type="bk:bookstoreType"/> <!-- ISBN格式验证 --> <xs:simpleType name="isbnType"> <xs:restriction base="xs:string"> <xs:pattern value="d{3}-d{10}|d{13}"/> </xs:restriction> </xs:simpleType> </xs:schema> 

总结

通过本文的详细讲解,您应该已经掌握了DTD到XSD转换的核心技巧。从基础语法对比到实战代码示例,我们涵盖了:

  1. 基础语法:理解DTD和XSD的基本结构和元素定义方式
  2. 转换规则:掌握元素、属性、实体和复杂结构的映射方法
  3. 实战案例:通过三个完整案例演示转换过程
  4. 自动化工具:了解XSLT和第三方工具的使用
  5. 常见问题:解决转换过程中的典型难题
  6. 验证测试:确保转换结果的正确性
  7. 高级技巧:利用XSD的高级特性提升模式质量

关键要点回顾:

  • XSD提供了更强的数据类型支持和命名空间功能
  • 转换时要保持语义等价,同时利用XSD的增强特性
  • 使用自动化工具可以提高效率,但手动验证必不可少
  • 良好的文档和版本控制是成功转换的保障

下一步建议:

  1. 实践转换您自己的DTD文件
  2. 学习XSD 1.1的高级特性
  3. 探索与其他模式语言(如RelaxNG)的互操作
  4. 在实际项目中应用这些技巧

通过不断练习和实践,您将能够熟练地将任何DTD转换为功能强大、类型安全的XSD模式,为现代XML应用奠定坚实基础。