分享

XML的校验方式

 KentProgram 2014-06-21

XML=可扩展标记语言(eXtensible Markup Language).
可扩展标记语言XML是一种简单的
数据存储语言,使用一系列简单的标记描述数据,而这些标记可以用方便的方式建立,虽然XML占用的空间比二进制数据要占用更多的空间,但XML极其简单易于掌握和使用。
XML的简单使其易于在任何应用程序中读写数据,这使XML很快成为数据交换的唯一公共语言,虽然不同的应用软件也支持其它的数据交换格式,但不久之后他们都将支持XML,那就意味着程序可以更容易的与Windows、Mac OS, Linux以及其他平台下产生的信息结合,然后可以很容易加载XML数据到程序中并分析他,并以XML格式输出结果。XML的前身是SGML(The Standard Generalized Markup Language),是自IBM从60年代就开始发展的GML(Generalized Markup Language)
同HTML一样, XML (可扩展标识语言)是通用标识语言标准(SGML)的一个子集,它是描述网络上的数据内容和结构的标准。尽管如此,XML不象HTML,HTML仅仅提供了在页面上显示信息的通用方法(没有上下文相关和动态功能) ,XML则对数据赋予上下文相关功能,它继承了SGML的大部分功能,却使用了不太复杂的技术。
为了使得SGML显得用户友好,XML重新定义了SGML的一些内部值和参数,去掉了大量的很少用到的功能,这些繁杂的功能使得SGML在设计网站时显得复杂化。XML保留了SGML的结构化功能,这样就使得网站设计者可以定义自己的文档类型,XML同时也推出一种新型文档类型,使得开发者也可以不必定义文档类型。
因为XML是W3C制定的,XML的
标准化工作由W3C的XML工作组负责,该小组成员由来自各个地方和行业的专家组成,他们通过email交流对XML标准的意见,并提出自己的看法 (www.w3.org/TR/WD-xml)。因为XML 是个公共格式, (它不专属于任何一家公司),你不必担心XML技术会成为少数公司的盈利工具,XML不是一个依附于特定浏览器的语言


DTD=类型定义(Documnet Type Definition)
DTD 是一套关于标记符的语法规则。它是XML1.0版规格得一部分,是XML文件的验证机制,属于XML文件组成的一部分。
DTD 是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。
XML文件提供应用程序一个数据交换的格式,DTD正是让XML文件能够成为数据交换的标准,因为不同的公司只需定义好标准的DTD,各公司都能够依照DTD建立XML文件,并且进行验证,如此就可以轻易的建立标准和交换数据,这样满足了
网络共享和数据交互。但是DTD 是使用非 XML 语法编写的
DTD 不可扩展,不支持命名空间,只提供非常有限的
数据类型


XSD=XML结构定义 ( XML Schemas Definition )
它是DTD的
替代品。XML Schema语言也就是XSD。XML Schema描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,以检查该XML文档是否符合其要求。文档设计者可以通过XML Schema指定一个XML文档所允许的结构和内容,并可据此检查一个XML文档是否是有效的。XML Schema本身是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析它。
    一个XML Schema会定义:文档中出现的元素、文档中出现的属性、子元素、子元素的数量、子元素的顺序、元素是否为空、元素和属性的数据类型、元素或属性的默认和固定值。
    XSD是DTD替代者的原因,一是据将来的条件可扩展,二是比DTD丰富和有用,三是用XML书写,四是支持数据类型,五是支持命名空间。
XML Schema的优点:
1) XML Schema基于XML,没有专门的语法
2) XML可以象其他XML文件一样解析和处理
3) XML Schema支持一系列的数据类型(int、float、Boolean、date等)
4) XML Schema提供可扩充的数据模型。
5) XML Schema支持综合命名空间
6) XML Schema支持属性组。



XSL=可扩展样式表语言 (EXtensible Stylesheet Language)
XSL 之于 XML ,就像 CSS 之于 HTML。它是指可扩展样式表语言 (EXtensible Stylesheet Language)。这是一种用于以可读格式呈现 XML 数据的语言。

XDR=XML数据简化构架定义(XML-Data Reduced),用来校验一个XML文件。

 

校验XML文件的方式有XSD 和XDR两种方式,这两种方式是利用XML Schema来实现的,现在我们介绍另外的一种校验:DTD。 DTD(Document type definition) 校验是使用W3C中有效性约束来实现的。DTD使用一种正式的文法来描述相适应的XML文档的结构和语法。完成一个DTD校验,是利用XmlValidatingReader类。XmlValidatingReader类将DTD定义在DOCTYPE中。这个DOCTYPE定义的可以包括在线的DTD或是指向一个DTD文件。

在什么情况下使用DTD校验呢?使用DTD校验,可以使一个用户组中独立的不同的用户使用共同的DTD来交换数据。比如,你可以使用共同的DTD校验你所接受到的XML数据是不是有效,你也可以用DTD来校验你自己的数据。

下面我们用一个例子来说明DTD是如何使用的。

首先写一个HeadCount.dtd:

<!ELEMENT HeadCount (Name)*>//ELEMENT说明元素是HeadCount

<!ELEMENT Name (Name)*> //ELEMENT说明元素是Name


<!ATTLIST Name First CDATA #REQUIRED>//ATTLIST说明元素Name后面是属性First

<!ATTLIST Name Last CDATA #REQUIRED>//ATTLIST说明元素Name后面还有一个是属性Last


<!ATTLIST Name Relation (self | spouse | child) "self">

<!ENTITY MyFirst "Jeff">

<!ENTITY MyLast "Smith">


具体的DTD规范可以参考W3C规范。

还有一个待校验的XML文件HeadCount.xml:

<!DOCTYPE HeadCount SYSTEM "HeadCount.dtd">

<HeadCount>

<Name First="Waldo" Last="Pepper">

<Name First="Salt" Last="Pepper" Relation="spouse"/>

<Name First="Red" Last="Pepper" Relation="child"/>

</Name>

<Name First="&MyFirst;" Last="&MyLast;">

<Name First="Sharon" Last="&MyLast;" Relation="spouse"/>

<Name First="Morgan" Last="&MyLast;" Relation="child"/>

<Name First="Shelby" Last="&MyLast;" Relation="child"/>

</Name>

</HeadCount>


现在我们写一个cs文件来实现校验:

testDtd.cs

using System;

using System.IO;

using System.Xml;

using System.Xml.Schema;


class testDtd

{

public static void Main()

{

XmlTextReader tr = new XmlTextReader("HeadCount.xml");

XmlValidatingReader vr = new XmlValidatingReader(tr);


vr.ValidationType = ValidationType.DTD;

vr.ValidationEventHandler += new ValidationEventHandler (ValidationHandler);


while(vr.Read());

Console.WriteLine("Validation finished");

}


public static void ValidationHandler(object sender, ValidationEventArgs args)

{

Console.WriteLine("***Validation error");

Console.WriteLine("/tSeverity:{0}", args.Severity);

Console.WriteLine("/tMessage :{0}", args.Message);

}

}


运行这个程序,可以发现校验正确。

但如果我们把它改下呢?

我们将HeadCount.dtd改成:

<!ELEMENT HeadCount1 (Name)*>

<!ELEMENT Name (Name)*>

<!ATTLIST Name First CDATA #REQUIRED>

<!ATTLIST Name Last CDATA #REQUIRED>

<!ATTLIST Name Relation (self | spouse | child) "self">

<!ENTITY MyFirst "Jeff">

<!ENTITY MyLast "Smith">


既将第一个元素改成HeadCount1,再运行这个程序,结果如下:


***Validation error

Severity:Error

Message :The 'HeadCount' element is not declared. An error occurred at

file:///F:/writer/validReader/testDtd/HeadCount.xml, (2, 2).

Validation finished


可以发现HeadCount.xml文件中第一个元素不是所期待的HeadCount1,所以报错。


恢复HeadCount.dtd文件,将HeadCount.xml改一下:

<!DOCTYPE HeadCount SYSTEM "HeadCount.dtd">

<HeadCount>

<Name First1="Waldo" Last1="Pepper">

<Name First="Salt" Last="Pepper" Relation="spouse"/>

<Name First="Red" Last="Pepper" Relation="child"/>

</Name>

<Name First1="&MyFirst;" Last1="&MyLast;">

<Name First="Sharon" Last="&MyLast;" Relation="spouse"/>

<Name First="Morgan" Last="&MyLast;" Relation="child"/>

<Name First="Shelby" Last="&MyLast;" Relation="child"/>

</Name>

</HeadCount>


既将First改成First1,Last改成Last1,再运行这个程序,结果如下:

***Validation error

Severity:Error

Message :The 'HeadCount' element is not declared. An error occurred at

file:///F:/writer/validReader/testDtd/HeadCount.xml, (2, 2).

Validation finished


F:/writer/validReader/testDtd>testDtd

***Validation error

Severity:Error

Message :The 'First1' attribute is not declared. An error occurred at f

ile:///F:/writer/validReader/testDtd/HeadCount.xml, (3, 9).

***Validation error

Severity:Error

Message :The 'Last1' attribute is not declared. An error occurred at fi

le:///F:/writer/validReader/testDtd/HeadCount.xml, (3, 24).

***Validation error

Severity:Error

Message :The required attribute 'Last' is missing. An error occurred at

file:///F:/writer/validReader/testDtd/HeadCount.xml, (3, 4).

***Validation error

Severity:Error

Message :The 'First1' attribute is not declared. An error occurred at f

ile:///F:/writer/validReader/testDtd/HeadCount.xml, (7, 9).

***Validation error

Severity:Error

Message :The 'Last1' attribute is not declared. An error occurred at fi

le:///F:/writer/validReader/testDtd/HeadCount.xml, (7, 28).

***Validation error

Severity:Error

Message :The required attribute 'Last' is missing. An error occurred at

file:///F:/writer/validReader/testDtd/HeadCount.xml, (7, 4).

Validation finished

通过报出来的错误信息,我们可以得知,First1和Last1属性在DTD文件中没有定义,所以出错。

通过以上的测试,可以得知DTD校验的一些简单的规律,它同Xml Schema校验相辅相成,共同构成了XML 校验,保证XML数据的正确性,是XML成为数据承载和传输的有效而可靠的载体。


XML 问题 #9  SQL 查询中的 DTD 和 XML 文档

本专栏讨论了可以不依赖 RDBMS 生成可移植 XML 结果集的公众域 sql2dtd 和 sql2xml 实用程序。从关系数据库中抽取数据的 SQL 查询可以提供非常实用且特殊的文档类型信息,用于以 XML 表示查询结果。
前一“XML 话题”专栏讨论了各种数据模型的一些基本理论和它们的优势。得出的一个结论就是 RDBMS 已被普遍接受(有充分的理由),并且在这种环境中,最好将 XML 看作是在各种 DBMS 之间传送数据的一种方法,而不是替代 DBMS 的事物。虽然 XPath 和 XSLT 可用于某些特定的“数据查询”,但它们的应用远不及 RDBMS 和(尤其是)SQL 的应用广泛和普遍。不过,由于篇幅限制,我将在以后的专栏中讨论 XPath 和 XSLT 的具体性能(和限制)。

 

一些近期的 RDBMS(至少包括 DB2、Oracle,可能还有其它一些 RDBMS)都附带了用于导出 XML 的内置(或者至少可选的)工具。不过,在本专栏中讨论的工具都具有普遍性;特别是,由这些工具生成的 DTD 对于对不同 RDBMS 执行的相同查询都是完全相同的。我希望这对实现数据透明性的目标会有所帮助。

过于简化
对于将关系数据库数据转换成 XML,您能想像到的最明显的方式通常也不是什么好的想法。即,它简单到 -- 无论从概念上还是从实践上 -- 将 RDBMS 的所有内容逐个表地转储到相应的 XML 文档中。例如,LIBRARY 数据库(继续上一专栏中那个简单示例)有一个称作 BOOKPRICE 的表,其内容如下:

SELECT * FROM BOOKPRICE;
+------------+-------+
| ISBN | Price |
+------------+-------+
| 2994927282 | 34.99 |
| 3920202049 | 47.50 |
+------------+-------+

 

 

我可以直接将它转换成如下文档 BOOKPRICE.xml:

清单 1. BOOKPRICE.xml 文档

<?xml version="1.0"?>
<SQL>
 <row>
 <ISBN>2994927282</ISBN>
 <Price>34.99</Price>
 </row>
 <row>
 <ISBN>3920202049</ISBN>
 <Price>47.50</Price>
 </row>
</SQL>


 

 

为数据库中的每个表创建了类似的 XML 文档后,您就拥有了整个数据库的快照。

适当简化
上面勾画的方法有两个基本问题。第一个问题是 XML 转储的效率非常低。您已经知道,XML 是一种非常冗长的格式,大型数据库的 XML 转储会生成更大的 XML 文档集合。而且,一些主要 DBMS 已经提供了简洁而有效的文本文件或压缩记录样式的转储。上面所勾画的 XML 对这些简单格式不但没有提供任何额外功能,反而显著地增加了传送文件的大小。

第二个问题比第一个更有趣。在某种意义上,“DASD 很廉价”,带宽也变得越来越廉价("DASD" 是 IBM 对“硬盘”的旧称;这个措词在设计上很陈腐)。因此单单效率不一定就是最重要的因素。更为重要的是,您几乎不会想要将数据库的全部内容与伙伴/部门/用户等进行交流。有时候,一些内容是专用的;而几乎任何时候,大多数内容与特定用户并没有任何关系。

将有用的 SQL 查询结果转储到 XML 中比将原始表转储到 XML 更有意义。而为了将 XML 事务或馈送所需的确切语法分析和处理的预期规律化,将 DTD 与这些有用的查询进行关联仍然会比较好。这两种有趣的操作恰好是公众域 Python 实用程序 sql2dtd 和 sql2xml 能够实现的。

假设 A 和 B 各自有其自身的内部数据存储器策略(例如,在不同的 RDBMS 中)。各自维护所有种类的相关信息,这些信息与 A 和 B 之间的交互没有关系,但它们又都具有一些希望共享的信息。假设,遵循这样的路线,A 需要与 B 循环地就特定种类的数据集进行通信。A 和 B 可以做的一件事是同意 A 将定期向 B 发送一组 XML 文档,每个文档都符合事先一致认可的 DTD。一个传输中的特定数据将随时间而有所不同,但有效性规则已事先指定。只要 A 和 B 知道它们之间的协议,就可以执行它们的编程。

生成 DTD
开发 A 和 B 之间的这种通信的一个方法是开发与 A 和 B 特定需要相匹配的 DTD(或模式)。然后,A 需要开发定制代码来将数据从 A 的当前 RDBMS 导出到符合一致的 DTD 中;B 需要开发定制代码来导入相同的数据(到结构不同的数据库中)。最后,通信通道被打开。

不过通常存在一种更快速的方法 -- 往往利用现有导出/导入过程的一种方法。“标准查询语言 (SQL)”是一种能够确切表达 RDBMS 数据库中您所感兴趣的数据的极其简洁的方法。尝试将例如 XPath 或 XSLT 这样的 XML 原始技术合并到关系模型上可能不太自然,尽管它们必定可以在 XML 的基本分层模型中表达查询功能。

许多组织已经开发出为实现已知任务的、经过了彻底测试的几组 SQL 语句。事实上,RDBMS 往往提供了用于优化已存储查询的方法。虽然肯定有一些为数据交换而设计的丰富的 DTD 比较有意义的情况,但在许多或者大多数情况下,在 SQL 查询中隐式地使用结构信息作为 SQL 数据传输的(自动)基础是种很好的解决方案。

虽然 SQL 查询可以以复杂的方法来组合表数据,但所有 SQL 查询的结果都是相当简单的“行与列”的排列。在查询输出中,列的数量是固定的,每行填入了每个固定列的值。(即,除了数量上没有改变以外,无论是值的类型还是列名在 SQL 结果中均无改变 -- 即使它们可能在 XML 文档中有所改变)。XML 表示元素复杂嵌套模式的潜力在表示 SQL 结果中并没有得到深层的体现。虽然如此,SQL 查询的一些重要方面可以而且也应该在简单的行/列位置以外以 XML DTD 表示。

要表示 SQL 查询中的哪些内容?
我认为在 A 和 B 之间预期的数据交换中,应该区分出两方面。一方面,是 A 数据的内部组织 -- 例如,其标准化和取消标准化优化。B 没有,也不需要考虑 A 的内在方面。另一方面,存在描述实际发送内容的元数据。当然,区分这些方面不一定很容易。

在编写 sql2dtd(和帮助规划 Scott Hathaway 的 sql2xml)时,我做了一些决定,确定了哪些属于数据传输的部分,哪些位于发送方设置的内部(不需要以 DTD 表示)。清单 2 所显示的样本 XML(带有作为内部集的 DTD)输出有助于说明这些决定(该输出完全从作为属性包含的 SQL 查询生成;当然,是在对合适的数据库运行时):

清单 2. 带有作为内部集的 DTD 的样本 XML 输出

<?xml version="1.0"?>
<!DOCTYPE SQL [
<!ELEMENT SQL (row)*>
<!ATTLIST SQL
 GROUP_BY NMTOKEN #FIXED "AuthID"
 query CDATA #FIXED "SELECT AuthID AS SSN,COUNT(GroupID)
 FROM AUTHGROUP GROUP BY AuthID
 ORDER BY AuthID"
>
<!ELEMENT row (SSN, column2)>
<!ATTLIST row num ID #IMPLIED>
<!ELEMENT SSN (#PCDATA)>
<!ELEMENT column2 (#PCDATA)>
<!ATTLIST column2 CALC CDATA #FIXED "COUNT(GroupID)">
]>

<SQL>
 <row num="1">
 <SSN>111-22-3333</SSN>
 <column2>1</column2>
 </row>
 <row num="2">
 <SSN>333-22-4444</SSN>
 <column2>2</column2>
 </row>
 <row num="3">
 <SSN>666-44-5555</SSN>
 <column2>1</column2>
 </row>
</SQL>


 

 

这个简单的 XML 文档实际上可以包含比人们最初时所注意到的更多的元数据。当然,通过将 SQL 本身作为根节点的一个属性包含在内,人们可以重新构造 SQL 中固有的任何事物。但这样做需要对 SQL 重新进行语法分析,sql2dtd 已在文档中表示过它,因此通常这没有必要。

CALC 属性的规范包含这样一个事实,即 XML 包含了计算过的元素。因为计算过的表达式可能很长,并且可能包含对于 XML 标记来说非法的字符,所以计算过的列仅由它们的位置命名。不过,加入元素内容的特定计算是作为标记的一个属性包含在内的。为了避免在 XML 主体中重复属性,它在 DTD 中指定为 #FIXED。

如果使用计算过的列,计算往往反映使用 "GROUP BY" 修饰符对列所进行的分组。所有这样的分组都在根元素的 GROUP_BY 属性中列出。

而且,如果使用了 "ORDER BY" 子句,每个 <row> 标记就会带有指定输出数据序列的 num 属性。不过,如果结果集是无序的,则不使用 num 属性。

让我们考虑一下在 DTD 中有什么东西没有表示,就会发现它实际上确实属于 A 的内部数据表示,而不属于发送的消息。

除了嵌入的原始 SQL 查询以外,没有保留用于查询数据的一个或多个表的表示("FROM" 子句)。特殊的表组织只不过不是 B 需要感兴趣的一些事物。事实上,A 可以在有适当传输协议之后彻底地修改其数据库设计;但只要通过一些手段抽取相同的字段(列),B 就不需要担心这一点。尤其是,因为 "AS" 子句覆盖了实际的表的列名,所以有可能继续发送在 A 数据库中不再拥有任何直接文字意义的 XML 元素。

在 sql2dtd 设计中最重要的一点是忽略了 "WHERE" 和 "HAVING" 子句(以及 JOIN、DISTINCT 和 ALL 修饰符)。和表名一样,将数据从 A 的那些表中取出所必需的特定联接和过滤器不是 B 应该担心的问题。如果 A 碰巧需要将一些表联接在一起以获得一些数据,那只是 A 的一个标准化策略。B 可以使用也可以不使用任何类似的策略(对于不同的数据子集),两种方法都不用担心 A 的操作。出于相关但稍微不同的理由忽略过滤器(主要使用 "WHERE" 子句或 "DISTINCT" 修饰符)。不管出于何种商业原因,如果 A 只需要告之 B 那些其 whatzit 大于 25 的 woozles,从 B 的角度来说,它只是属于 woozles 的性质。即,A 可能对 B 不关心的 woozles 的一个子类感兴趣;但是 A 需要通过使用过滤器来获得感兴趣的内容(与不拥有它们,或将它们放在另一个表中相反)这一特定事实并不是 B 需要担心的。从这一方面来说,sub-select 只是另一种过滤器。

结束
我还没有介绍 sql2dtd 和 sql2xml 的任何特定用法。这里不需要做太多解释,因为它们自己已在内部详细说明了。一般情况下, sql2dtd 可以从 SQL 查询生成 DTD,但它本身并不查询任何数据库。sql2xml 通过 ODBC 执行查询,并可以利用 sql2dtd 来获得 DTD(它也可以生成 DTD 较少的 XML文档)。

这些工具只对 A 和 B 之间预期大约一半过程有所帮助。A 和 B 可以快速地使用这些工具达到 DTD,A 可以同样快速地生成符合这些 DTD 的输出 XML 文档。但 B 最终仍需要执行语法分析、存储和处理这些接收到的文档所涉及的所有工作。以后的专栏将更详细地讨论 B 的任务。


简单的DTD例子描述和分析


 下面举一个带有内部DTD的XML文档的例子: 

  <?xml version="1.0" encoding="GB2312" ?> 
  <!DOCTYPE 家庭 [ 
   <!ELEMENT 家庭 (人+,家电*)> 
   <!ELEMENT人 EMPTY> 
   <!ELEMENT 家电 EMPTY> 
   <!ATTLIST 人 
    名字 CDATA #REQUIRED 
    性别 (男|女) #REQUIRED 
    年龄 CDATA #REQUIRED 
    爱好 CDATA #IMPLIED 
   > 
   <!ATTLIST 家电 
     名称 CDATA #REQUIRED 
     数量 CDATA #REQUIRED 
     说明 CDATA #IMPLIED 
    > 
  ]>  
  <家庭> 
  <人> 
  <名字>郭大路 
  <性别>男 
  <年龄>25 
   
  <人> 
  <名字>李寻欢 
  <性别>男 
  <年龄>38 
  <爱好>作个教育家和伟人 
   
  <家电> 
  <名称>彩电 
  <数量>3 
   
   

  这个文档从第二行开始进行文档类型声明,包含了文档元素(家庭)的名称。根据定义,我们发现该元素可以包含一个或者多个人(由这个+号决定的),可以包含零个或多个家电(由这个*符号决定),然后定义了人这个元素的需要的属性,其中名字、性别和年龄是必须的,而爱好可以填有也可以不填。家电的名字和数量属性必须有,但说明可以写也可以不写。 

  如果采用外部DTD的话,就需要有两个文档,第一个文档就是关于DTD的文档,第二个文档就是遵守DTD格式的内容文档。实际上我们可以建立无穷多个遵守该DTD格式的文档。举一个例子来说,我们在构造关系数据库中的表的时候,我们需要定义好表的结构(也就是表包含的字段集合),然后我们就可以往这个表中放入记录,记录的个数从理论上讲可以是无穷多个的。这里关于表的结构就类似于DTD文档。记录类似于遵守DTD格式的内容文档。外部DTD的好处是:它可以方便高效地被多个XML文件所共享。你只要写一个DTD文件,就可以被多个XML文件所引用。事实上,当许多组织需要统一它们的数据交换格式时,它们就是通过外部DTD来完成的。这样做不仅简化了输入工作,还保证当你需要对DTD做出改动时,不用一一去改每个引用了它的XML文件,只要改一个公用的DTD文件就足够了。不过需要注意,如果DTD的改动不是"向后兼容"的,这时原先根据该DTD编写的那些XML文件可能就会出问题了。 

  现在我们就尝试建立一个DTD文档,不妨命名为Home.dtd。其代码如下: 

  <?xml version="1.0" encoding="GB2312" ?> 
  <!ELEMENT 家庭 (人+,家电*)> 
  <!ELEMENT人 EMPTY> 
  <!ELEMENT 家电 EMPTY> 
  <!ATTLIST 人 
    名字 CDATA #REQUIRED 
    性别 (男|女) #REQUIRED 
    年龄 CDATA #REQUIRED 
    爱好 CDATA #IMPLIED 
   > 
  <!ATTLIST 家电 
   名称 CDATA #REQUIRED 
   数量 CDATA #REQUIRED 
   说明 CDATA #IMPLIED 
  > 

  然后,我们可以建立一个遵守该DTD格式的内容文档,不妨设为HomeInstance.xml。其代码如下: 

  <?xml version="1.0" encoding="GB2312" ?> 
  <!DOCTYPE 家庭 SYSTEM "Home.dtd"> 
  <家庭> 
  <人> 
  <名字>郭大路 
  <性别>男 
  <年龄>25 
   
  <人> 
  <名字>李寻欢 
  <性别>男 
  <年龄>38 
  <爱好>作个教育家和伟人 
   
  <家电> 
  <名称>彩电 
  <数量>3 
   
   

  把这两个文档放到同一个目录下,然后可以用XML浏览器对HomeInstance.xml进行浏览,结果应该和使用内部DTD的结果一样。 

  众所周知,在设计MIS应用程序的时候,重要的是要进行E-R图设计,然后建立关系数据库,建立数据库的关键就是要定义好表的格式,并使它的范式尽可能的高。对应的,建立基于XML应用的关键就是要定义好DTD,然后所有的内容就按照DTD格式进行编写。DTD实际上表现了一个层次的关系,你也可以把它理解成一棵树的结构。树中的节点实际上就是一个个元素(ELEMENT),一个元素可以包含其他的元素。比如上面的例子中家庭这个元素包含了人和家电这两个元素。一个元素可以包含属性(ATTLIST)也可以没有任何属性。比如上面的例子中,家庭这个元素就没有任何属性,而人和家电都有自己的属性。 

  际上如果大家学过编译系统的话,都知道对编程语言进行语法定义的工具:巴科斯-诺尔范式。它是用来对语言的语法进行定义的工具。实际上DTD就是起到了类似的作用。

XML=可扩展标记语言(eXtensible Markup Language).
可扩展标记语言XML是一种简单的
数据存储语言,使用一系列简单的标记描述数据,而这些标记可以用方便的方式建立,虽然XML占用的空间比二进制数据要占用更多的空间,但XML极其简单易于掌握和使用。
XML的简单使其易于在任何应用程序中读写数据,这使XML很快成为数据交换的唯一公共语言,虽然不同的应用软件也支持其它的数据交换格式,但不久之后他们都将支持XML,那就意味着程序可以更容易的与Windows、Mac OS, Linux以及其他平台下产生的信息结合,然后可以很容易加载XML数据到程序中并分析他,并以XML格式输出结果。XML的前身是SGML(The Standard Generalized Markup Language),是自IBM从60年代就开始发展的GML(Generalized Markup Language)
同HTML一样, XML (可扩展标识语言)是通用标识语言标准(SGML)的一个子集,它是描述网络上的数据内容和结构的标准。尽管如此,XML不象HTML,HTML仅仅提供了在页面上显示信息的通用方法(没有上下文相关和动态功能) ,XML则对数据赋予上下文相关功能,它继承了SGML的大部分功能,却使用了不太复杂的技术。
为了使得SGML显得用户友好,XML重新定义了SGML的一些内部值和参数,去掉了大量的很少用到的功能,这些繁杂的功能使得SGML在设计网站时显得复杂化。XML保留了SGML的结构化功能,这样就使得网站设计者可以定义自己的文档类型,XML同时也推出一种新型文档类型,使得开发者也可以不必定义文档类型。
因为XML是W3C制定的,XML的
标准化工作由W3C的XML工作组负责,该小组成员由来自各个地方和行业的专家组成,他们通过email交流对XML标准的意见,并提出自己的看法 (www.w3.org/TR/WD-xml)。因为XML 是个公共格式, (它不专属于任何一家公司),你不必担心XML技术会成为少数公司的盈利工具,XML不是一个依附于特定浏览器的语言


DTD=类型定义(Documnet Type Definition)
DTD 是一套关于标记符的语法规则。它是XML1.0版规格得一部分,是XML文件的验证机制,属于XML文件组成的一部分。
DTD 是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。
XML文件提供应用程序一个数据交换的格式,DTD正是让XML文件能够成为数据交换的标准,因为不同的公司只需定义好标准的DTD,各公司都能够依照DTD建立XML文件,并且进行验证,如此就可以轻易的建立标准和交换数据,这样满足了
网络共享和数据交互。但是DTD 是使用非 XML 语法编写的
DTD 不可扩展,不支持命名空间,只提供非常有限的
数据类型


XSD=XML结构定义 ( XML Schemas Definition )
它是DTD的
替代品。XML Schema语言也就是XSD。XML Schema描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,以检查该XML文档是否符合其要求。文档设计者可以通过XML Schema指定一个XML文档所允许的结构和内容,并可据此检查一个XML文档是否是有效的。XML Schema本身是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析它。
    一个XML Schema会定义:文档中出现的元素、文档中出现的属性、子元素、子元素的数量、子元素的顺序、元素是否为空、元素和属性的数据类型、元素或属性的默认和固定值。
    XSD是DTD替代者的原因,一是据将来的条件可扩展,二是比DTD丰富和有用,三是用XML书写,四是支持数据类型,五是支持命名空间。
XML Schema的优点:
1) XML Schema基于XML,没有专门的语法
2) XML可以象其他XML文件一样解析和处理
3) XML Schema支持一系列的数据类型(int、float、Boolean、date等)
4) XML Schema提供可扩充的数据模型。
5) XML Schema支持综合命名空间
6) XML Schema支持属性组。



XSL=可扩展样式表语言 (EXtensible Stylesheet Language)
XSL 之于 XML ,就像 CSS 之于 HTML。它是指可扩展样式表语言 (EXtensible Stylesheet Language)。这是一种用于以可读格式呈现 XML 数据的语言。

XDR=XML数据简化构架定义(XML-Data Reduced),用来校验一个XML文件。

 

校验XML文件的方式有XSD 和XDR两种方式,这两种方式是利用XML Schema来实现的,现在我们介绍另外的一种校验:DTD。 DTD(Document type definition) 校验是使用W3C中有效性约束来实现的。DTD使用一种正式的文法来描述相适应的XML文档的结构和语法。完成一个DTD校验,是利用XmlValidatingReader类。XmlValidatingReader类将DTD定义在DOCTYPE中。这个DOCTYPE定义的可以包括在线的DTD或是指向一个DTD文件。

在什么情况下使用DTD校验呢?使用DTD校验,可以使一个用户组中独立的不同的用户使用共同的DTD来交换数据。比如,你可以使用共同的DTD校验你所接受到的XML数据是不是有效,你也可以用DTD来校验你自己的数据。

下面我们用一个例子来说明DTD是如何使用的。

首先写一个HeadCount.dtd:

<!ELEMENT HeadCount (Name)*>//ELEMENT说明元素是HeadCount

<!ELEMENT Name (Name)*> //ELEMENT说明元素是Name


<!ATTLIST Name First CDATA #REQUIRED>//ATTLIST说明元素Name后面是属性First

<!ATTLIST Name Last CDATA #REQUIRED>//ATTLIST说明元素Name后面还有一个是属性Last


<!ATTLIST Name Relation (self | spouse | child) "self">

<!ENTITY MyFirst "Jeff">

<!ENTITY MyLast "Smith">


具体的DTD规范可以参考W3C规范。

还有一个待校验的XML文件HeadCount.xml:

<!DOCTYPE HeadCount SYSTEM "HeadCount.dtd">

<HeadCount>

<Name First="Waldo" Last="Pepper">

<Name First="Salt" Last="Pepper" Relation="spouse"/>

<Name First="Red" Last="Pepper" Relation="child"/>

</Name>

<Name First="&MyFirst;" Last="&MyLast;">

<Name First="Sharon" Last="&MyLast;" Relation="spouse"/>

<Name First="Morgan" Last="&MyLast;" Relation="child"/>

<Name First="Shelby" Last="&MyLast;" Relation="child"/>

</Name>

</HeadCount>


现在我们写一个cs文件来实现校验:

testDtd.cs

using System;

using System.IO;

using System.Xml;

using System.Xml.Schema;


class testDtd

{

public static void Main()

{

XmlTextReader tr = new XmlTextReader("HeadCount.xml");

XmlValidatingReader vr = new XmlValidatingReader(tr);


vr.ValidationType = ValidationType.DTD;

vr.ValidationEventHandler += new ValidationEventHandler (ValidationHandler);


while(vr.Read());

Console.WriteLine("Validation finished");

}


public static void ValidationHandler(object sender, ValidationEventArgs args)

{

Console.WriteLine("***Validation error");

Console.WriteLine("/tSeverity:{0}", args.Severity);

Console.WriteLine("/tMessage :{0}", args.Message);

}

}


运行这个程序,可以发现校验正确。

但如果我们把它改下呢?

我们将HeadCount.dtd改成:

<!ELEMENT HeadCount1 (Name)*>

<!ELEMENT Name (Name)*>

<!ATTLIST Name First CDATA #REQUIRED>

<!ATTLIST Name Last CDATA #REQUIRED>

<!ATTLIST Name Relation (self | spouse | child) "self">

<!ENTITY MyFirst "Jeff">

<!ENTITY MyLast "Smith">


既将第一个元素改成HeadCount1,再运行这个程序,结果如下:


***Validation error

Severity:Error

Message :The 'HeadCount' element is not declared. An error occurred at

file:///F:/writer/validReader/testDtd/HeadCount.xml, (2, 2).

Validation finished


可以发现HeadCount.xml文件中第一个元素不是所期待的HeadCount1,所以报错。


恢复HeadCount.dtd文件,将HeadCount.xml改一下:

<!DOCTYPE HeadCount SYSTEM "HeadCount.dtd">

<HeadCount>

<Name First1="Waldo" Last1="Pepper">

<Name First="Salt" Last="Pepper" Relation="spouse"/>

<Name First="Red" Last="Pepper" Relation="child"/>

</Name>

<Name First1="&MyFirst;" Last1="&MyLast;">

<Name First="Sharon" Last="&MyLast;" Relation="spouse"/>

<Name First="Morgan" Last="&MyLast;" Relation="child"/>

<Name First="Shelby" Last="&MyLast;" Relation="child"/>

</Name>

</HeadCount>


既将First改成First1,Last改成Last1,再运行这个程序,结果如下:

***Validation error

Severity:Error

Message :The 'HeadCount' element is not declared. An error occurred at

file:///F:/writer/validReader/testDtd/HeadCount.xml, (2, 2).

Validation finished


F:/writer/validReader/testDtd>testDtd

***Validation error

Severity:Error

Message :The 'First1' attribute is not declared. An error occurred at f

ile:///F:/writer/validReader/testDtd/HeadCount.xml, (3, 9).

***Validation error

Severity:Error

Message :The 'Last1' attribute is not declared. An error occurred at fi

le:///F:/writer/validReader/testDtd/HeadCount.xml, (3, 24).

***Validation error

Severity:Error

Message :The required attribute 'Last' is missing. An error occurred at

file:///F:/writer/validReader/testDtd/HeadCount.xml, (3, 4).

***Validation error

Severity:Error

Message :The 'First1' attribute is not declared. An error occurred at f

ile:///F:/writer/validReader/testDtd/HeadCount.xml, (7, 9).

***Validation error

Severity:Error

Message :The 'Last1' attribute is not declared. An error occurred at fi

le:///F:/writer/validReader/testDtd/HeadCount.xml, (7, 28).

***Validation error

Severity:Error

Message :The required attribute 'Last' is missing. An error occurred at

file:///F:/writer/validReader/testDtd/HeadCount.xml, (7, 4).

Validation finished

通过报出来的错误信息,我们可以得知,First1和Last1属性在DTD文件中没有定义,所以出错。

通过以上的测试,可以得知DTD校验的一些简单的规律,它同Xml Schema校验相辅相成,共同构成了XML 校验,保证XML数据的正确性,是XML成为数据承载和传输的有效而可靠的载体。


XML 问题 #9  SQL 查询中的 DTD 和 XML 文档

本专栏讨论了可以不依赖 RDBMS 生成可移植 XML 结果集的公众域 sql2dtd 和 sql2xml 实用程序。从关系数据库中抽取数据的 SQL 查询可以提供非常实用且特殊的文档类型信息,用于以 XML 表示查询结果。
前一“XML 话题”专栏讨论了各种数据模型的一些基本理论和它们的优势。得出的一个结论就是 RDBMS 已被普遍接受(有充分的理由),并且在这种环境中,最好将 XML 看作是在各种 DBMS 之间传送数据的一种方法,而不是替代 DBMS 的事物。虽然 XPath 和 XSLT 可用于某些特定的“数据查询”,但它们的应用远不及 RDBMS 和(尤其是)SQL 的应用广泛和普遍。不过,由于篇幅限制,我将在以后的专栏中讨论 XPath 和 XSLT 的具体性能(和限制)。

 

一些近期的 RDBMS(至少包括 DB2、Oracle,可能还有其它一些 RDBMS)都附带了用于导出 XML 的内置(或者至少可选的)工具。不过,在本专栏中讨论的工具都具有普遍性;特别是,由这些工具生成的 DTD 对于对不同 RDBMS 执行的相同查询都是完全相同的。我希望这对实现数据透明性的目标会有所帮助。

过于简化
对于将关系数据库数据转换成 XML,您能想像到的最明显的方式通常也不是什么好的想法。即,它简单到 -- 无论从概念上还是从实践上 -- 将 RDBMS 的所有内容逐个表地转储到相应的 XML 文档中。例如,LIBRARY 数据库(继续上一专栏中那个简单示例)有一个称作 BOOKPRICE 的表,其内容如下:

SELECT * FROM BOOKPRICE;
+------------+-------+
| ISBN | Price |
+------------+-------+
| 2994927282 | 34.99 |
| 3920202049 | 47.50 |
+------------+-------+

 

 

我可以直接将它转换成如下文档 BOOKPRICE.xml:

清单 1. BOOKPRICE.xml 文档

<?xml version="1.0"?>
<SQL>
 <row>
 <ISBN>2994927282</ISBN>
 <Price>34.99</Price>
 </row>
 <row>
 <ISBN>3920202049</ISBN>
 <Price>47.50</Price>
 </row>
</SQL>


 

 

为数据库中的每个表创建了类似的 XML 文档后,您就拥有了整个数据库的快照。

适当简化
上面勾画的方法有两个基本问题。第一个问题是 XML 转储的效率非常低。您已经知道,XML 是一种非常冗长的格式,大型数据库的 XML 转储会生成更大的 XML 文档集合。而且,一些主要 DBMS 已经提供了简洁而有效的文本文件或压缩记录样式的转储。上面所勾画的 XML 对这些简单格式不但没有提供任何额外功能,反而显著地增加了传送文件的大小。

第二个问题比第一个更有趣。在某种意义上,“DASD 很廉价”,带宽也变得越来越廉价("DASD" 是 IBM 对“硬盘”的旧称;这个措词在设计上很陈腐)。因此单单效率不一定就是最重要的因素。更为重要的是,您几乎不会想要将数据库的全部内容与伙伴/部门/用户等进行交流。有时候,一些内容是专用的;而几乎任何时候,大多数内容与特定用户并没有任何关系。

将有用的 SQL 查询结果转储到 XML 中比将原始表转储到 XML 更有意义。而为了将 XML 事务或馈送所需的确切语法分析和处理的预期规律化,将 DTD 与这些有用的查询进行关联仍然会比较好。这两种有趣的操作恰好是公众域 Python 实用程序 sql2dtd 和 sql2xml 能够实现的。

假设 A 和 B 各自有其自身的内部数据存储器策略(例如,在不同的 RDBMS 中)。各自维护所有种类的相关信息,这些信息与 A 和 B 之间的交互没有关系,但它们又都具有一些希望共享的信息。假设,遵循这样的路线,A 需要与 B 循环地就特定种类的数据集进行通信。A 和 B 可以做的一件事是同意 A 将定期向 B 发送一组 XML 文档,每个文档都符合事先一致认可的 DTD。一个传输中的特定数据将随时间而有所不同,但有效性规则已事先指定。只要 A 和 B 知道它们之间的协议,就可以执行它们的编程。

生成 DTD
开发 A 和 B 之间的这种通信的一个方法是开发与 A 和 B 特定需要相匹配的 DTD(或模式)。然后,A 需要开发定制代码来将数据从 A 的当前 RDBMS 导出到符合一致的 DTD 中;B 需要开发定制代码来导入相同的数据(到结构不同的数据库中)。最后,通信通道被打开。

不过通常存在一种更快速的方法 -- 往往利用现有导出/导入过程的一种方法。“标准查询语言 (SQL)”是一种能够确切表达 RDBMS 数据库中您所感兴趣的数据的极其简洁的方法。尝试将例如 XPath 或 XSLT 这样的 XML 原始技术合并到关系模型上可能不太自然,尽管它们必定可以在 XML 的基本分层模型中表达查询功能。

许多组织已经开发出为实现已知任务的、经过了彻底测试的几组 SQL 语句。事实上,RDBMS 往往提供了用于优化已存储查询的方法。虽然肯定有一些为数据交换而设计的丰富的 DTD 比较有意义的情况,但在许多或者大多数情况下,在 SQL 查询中隐式地使用结构信息作为 SQL 数据传输的(自动)基础是种很好的解决方案。

虽然 SQL 查询可以以复杂的方法来组合表数据,但所有 SQL 查询的结果都是相当简单的“行与列”的排列。在查询输出中,列的数量是固定的,每行填入了每个固定列的值。(即,除了数量上没有改变以外,无论是值的类型还是列名在 SQL 结果中均无改变 -- 即使它们可能在 XML 文档中有所改变)。XML 表示元素复杂嵌套模式的潜力在表示 SQL 结果中并没有得到深层的体现。虽然如此,SQL 查询的一些重要方面可以而且也应该在简单的行/列位置以外以 XML DTD 表示。

要表示 SQL 查询中的哪些内容?
我认为在 A 和 B 之间预期的数据交换中,应该区分出两方面。一方面,是 A 数据的内部组织 -- 例如,其标准化和取消标准化优化。B 没有,也不需要考虑 A 的内在方面。另一方面,存在描述实际发送内容的元数据。当然,区分这些方面不一定很容易。

在编写 sql2dtd(和帮助规划 Scott Hathaway 的 sql2xml)时,我做了一些决定,确定了哪些属于数据传输的部分,哪些位于发送方设置的内部(不需要以 DTD 表示)。清单 2 所显示的样本 XML(带有作为内部集的 DTD)输出有助于说明这些决定(该输出完全从作为属性包含的 SQL 查询生成;当然,是在对合适的数据库运行时):

清单 2. 带有作为内部集的 DTD 的样本 XML 输出

<?xml version="1.0"?>
<!DOCTYPE SQL [
<!ELEMENT SQL (row)*>
<!ATTLIST SQL
 GROUP_BY NMTOKEN #FIXED "AuthID"
 query CDATA #FIXED "SELECT AuthID AS SSN,COUNT(GroupID)
 FROM AUTHGROUP GROUP BY AuthID
 ORDER BY AuthID"
>
<!ELEMENT row (SSN, column2)>
<!ATTLIST row num ID #IMPLIED>
<!ELEMENT SSN (#PCDATA)>
<!ELEMENT column2 (#PCDATA)>
<!ATTLIST column2 CALC CDATA #FIXED "COUNT(GroupID)">
]>

<SQL>
 <row num="1">
 <SSN>111-22-3333</SSN>
 <column2>1</column2>
 </row>
 <row num="2">
 <SSN>333-22-4444</SSN>
 <column2>2</column2>
 </row>
 <row num="3">
 <SSN>666-44-5555</SSN>
 <column2>1</column2>
 </row>
</SQL>


 

 

这个简单的 XML 文档实际上可以包含比人们最初时所注意到的更多的元数据。当然,通过将 SQL 本身作为根节点的一个属性包含在内,人们可以重新构造 SQL 中固有的任何事物。但这样做需要对 SQL 重新进行语法分析,sql2dtd 已在文档中表示过它,因此通常这没有必要。

CALC 属性的规范包含这样一个事实,即 XML 包含了计算过的元素。因为计算过的表达式可能很长,并且可能包含对于 XML 标记来说非法的字符,所以计算过的列仅由它们的位置命名。不过,加入元素内容的特定计算是作为标记的一个属性包含在内的。为了避免在 XML 主体中重复属性,它在 DTD 中指定为 #FIXED。

如果使用计算过的列,计算往往反映使用 "GROUP BY" 修饰符对列所进行的分组。所有这样的分组都在根元素的 GROUP_BY 属性中列出。

而且,如果使用了 "ORDER BY" 子句,每个 <row> 标记就会带有指定输出数据序列的 num 属性。不过,如果结果集是无序的,则不使用 num 属性。

让我们考虑一下在 DTD 中有什么东西没有表示,就会发现它实际上确实属于 A 的内部数据表示,而不属于发送的消息。

除了嵌入的原始 SQL 查询以外,没有保留用于查询数据的一个或多个表的表示("FROM" 子句)。特殊的表组织只不过不是 B 需要感兴趣的一些事物。事实上,A 可以在有适当传输协议之后彻底地修改其数据库设计;但只要通过一些手段抽取相同的字段(列),B 就不需要担心这一点。尤其是,因为 "AS" 子句覆盖了实际的表的列名,所以有可能继续发送在 A 数据库中不再拥有任何直接文字意义的 XML 元素。

在 sql2dtd 设计中最重要的一点是忽略了 "WHERE" 和 "HAVING" 子句(以及 JOIN、DISTINCT 和 ALL 修饰符)。和表名一样,将数据从 A 的那些表中取出所必需的特定联接和过滤器不是 B 应该担心的问题。如果 A 碰巧需要将一些表联接在一起以获得一些数据,那只是 A 的一个标准化策略。B 可以使用也可以不使用任何类似的策略(对于不同的数据子集),两种方法都不用担心 A 的操作。出于相关但稍微不同的理由忽略过滤器(主要使用 "WHERE" 子句或 "DISTINCT" 修饰符)。不管出于何种商业原因,如果 A 只需要告之 B 那些其 whatzit 大于 25 的 woozles,从 B 的角度来说,它只是属于 woozles 的性质。即,A 可能对 B 不关心的 woozles 的一个子类感兴趣;但是 A 需要通过使用过滤器来获得感兴趣的内容(与不拥有它们,或将它们放在另一个表中相反)这一特定事实并不是 B 需要担心的。从这一方面来说,sub-select 只是另一种过滤器。

结束
我还没有介绍 sql2dtd 和 sql2xml 的任何特定用法。这里不需要做太多解释,因为它们自己已在内部详细说明了。一般情况下, sql2dtd 可以从 SQL 查询生成 DTD,但它本身并不查询任何数据库。sql2xml 通过 ODBC 执行查询,并可以利用 sql2dtd 来获得 DTD(它也可以生成 DTD 较少的 XML文档)。

这些工具只对 A 和 B 之间预期大约一半过程有所帮助。A 和 B 可以快速地使用这些工具达到 DTD,A 可以同样快速地生成符合这些 DTD 的输出 XML 文档。但 B 最终仍需要执行语法分析、存储和处理这些接收到的文档所涉及的所有工作。以后的专栏将更详细地讨论 B 的任务。


简单的DTD例子描述和分析


 下面举一个带有内部DTD的XML文档的例子: 

  <?xml version="1.0" encoding="GB2312" ?> 
  <!DOCTYPE 家庭 [ 
   <!ELEMENT 家庭 (人+,家电*)> 
   <!ELEMENT人 EMPTY> 
   <!ELEMENT 家电 EMPTY> 
   <!ATTLIST 人 
    名字 CDATA #REQUIRED 
    性别 (男|女) #REQUIRED 
    年龄 CDATA #REQUIRED 
    爱好 CDATA #IMPLIED 
   > 
   <!ATTLIST 家电 
     名称 CDATA #REQUIRED 
     数量 CDATA #REQUIRED 
     说明 CDATA #IMPLIED 
    > 
  ]>  
  <家庭> 
  <人> 
  <名字>郭大路 
  <性别>男 
  <年龄>25 
   
  <人> 
  <名字>李寻欢 
  <性别>男 
  <年龄>38 
  <爱好>作个教育家和伟人 
   
  <家电> 
  <名称>彩电 
  <数量>3 
   
   

  这个文档从第二行开始进行文档类型声明,包含了文档元素(家庭)的名称。根据定义,我们发现该元素可以包含一个或者多个人(由这个+号决定的),可以包含零个或多个家电(由这个*符号决定),然后定义了人这个元素的需要的属性,其中名字、性别和年龄是必须的,而爱好可以填有也可以不填。家电的名字和数量属性必须有,但说明可以写也可以不写。 

  如果采用外部DTD的话,就需要有两个文档,第一个文档就是关于DTD的文档,第二个文档就是遵守DTD格式的内容文档。实际上我们可以建立无穷多个遵守该DTD格式的文档。举一个例子来说,我们在构造关系数据库中的表的时候,我们需要定义好表的结构(也就是表包含的字段集合),然后我们就可以往这个表中放入记录,记录的个数从理论上讲可以是无穷多个的。这里关于表的结构就类似于DTD文档。记录类似于遵守DTD格式的内容文档。外部DTD的好处是:它可以方便高效地被多个XML文件所共享。你只要写一个DTD文件,就可以被多个XML文件所引用。事实上,当许多组织需要统一它们的数据交换格式时,它们就是通过外部DTD来完成的。这样做不仅简化了输入工作,还保证当你需要对DTD做出改动时,不用一一去改每个引用了它的XML文件,只要改一个公用的DTD文件就足够了。不过需要注意,如果DTD的改动不是"向后兼容"的,这时原先根据该DTD编写的那些XML文件可能就会出问题了。 

  现在我们就尝试建立一个DTD文档,不妨命名为Home.dtd。其代码如下: 

  <?xml version="1.0" encoding="GB2312" ?> 
  <!ELEMENT 家庭 (人+,家电*)> 
  <!ELEMENT人 EMPTY> 
  <!ELEMENT 家电 EMPTY> 
  <!ATTLIST 人 
    名字 CDATA #REQUIRED 
    性别 (男|女) #REQUIRED 
    年龄 CDATA #REQUIRED 
    爱好 CDATA #IMPLIED 
   > 
  <!ATTLIST 家电 
   名称 CDATA #REQUIRED 
   数量 CDATA #REQUIRED 
   说明 CDATA #IMPLIED 
  > 

  然后,我们可以建立一个遵守该DTD格式的内容文档,不妨设为HomeInstance.xml。其代码如下: 

  <?xml version="1.0" encoding="GB2312" ?> 
  <!DOCTYPE 家庭 SYSTEM "Home.dtd"> 
  <家庭> 
  <人> 
  <名字>郭大路 
  <性别>男 
  <年龄>25 
   
  <人> 
  <名字>李寻欢 
  <性别>男 
  <年龄>38 
  <爱好>作个教育家和伟人 
   
  <家电> 
  <名称>彩电 
  <数量>3 
   
   

  把这两个文档放到同一个目录下,然后可以用XML浏览器对HomeInstance.xml进行浏览,结果应该和使用内部DTD的结果一样。 

  众所周知,在设计MIS应用程序的时候,重要的是要进行E-R图设计,然后建立关系数据库,建立数据库的关键就是要定义好表的格式,并使它的范式尽可能的高。对应的,建立基于XML应用的关键就是要定义好DTD,然后所有的内容就按照DTD格式进行编写。DTD实际上表现了一个层次的关系,你也可以把它理解成一棵树的结构。树中的节点实际上就是一个个元素(ELEMENT),一个元素可以包含其他的元素。比如上面的例子中家庭这个元素包含了人和家电这两个元素。一个元素可以包含属性(ATTLIST)也可以没有任何属性。比如上面的例子中,家庭这个元素就没有任何属性,而人和家电都有自己的属性。 

  际上如果大家学过编译系统的话,都知道对编程语言进行语法定义的工具:巴科斯-诺尔范式。它是用来对语言的语法进行定义的工具。实际上DTD就是起到了类似的作用。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多