XML DTD

 

缩略词DTD代表文档类型定义。一项文档类型定义应规定元素清单、属性、标记、文档中的实体及其相互关系。DTD为文档结构制定了一套规则。例如,一项DTD指定一个BOOK元素有一个ISBN子元素、一个TITLE子元素、一个或多个AUTHOR子元素,有或没有SUBTITLE。DTD以元素、实体、属性和记号的标记声明来做到这一点。

进行合法性检查的语法分析程序读取DTD并检查文档是否合乎DTD指定的规则。在Web上可找到几十种不同的进行合法性检查的语法分析程序。其中多数是免费的。大多数是以库文件的形式存在的接近完成的产品,以便程序员可将其结合到自己的程序中。这些产品用户界面(如果有的话)较差。这类分析程序包括IBM的alphaWorks’XML for Java、Microsoft和DataChannel的XJParser和Silfide的SXP。

XML for Java:http://www.alphaworks.ibm.com/ tech/xml

XJParser:http://www.datachannel.com/xml_resources/

SXP:http://www.loria.fr/projets/XSilfide/EN/sxp/

一些库文件也包括在命令行上运行的独立的分析程序。这些程序读取XML文件并报告发现的错误,但不加以显示。例如,XJParse 是一个Java程序,包括在IBM的Samples. XJParse软件包中的XML for Java 1.1.16类库中。要运行这一程序,必须首先将XML for Java的jar文件添加到Java类库的路径上。

 

1. 元素声明

在合法的XML文档中使用的每项标记都要在DTD中的元素声明中加以声明。一项元素声明指明了元素名称和元素可能的内容。内容清单有时称为内容规格。内容规格使用一种简单的语法精确地指明文档中允许什么和不允许什么。这听起来复杂,却只需在元素名称上加上如*、?或+的标点以便指明它可能出现不止一次,可能出现或可能不出现,或必须出现至少一次。

1.1 ANY

要做的第一件事是标识基本元素,即root元素。

<!ELEMENT SEASON ANY>

所有的元素类型声明都以<!ELEMENT(区分大小写)开头而以>结束。其中包括声明的元素名称(本例中为SEASON)后接内容规格。关键词ANY(也要区分大小写)表明所有可能的元素以及可析的字符数据都可以是SEASON元素的子元素。

1.2 #PCDATA

<!ELEMENT YEAR (#PCDATA)>

该声明说明YEAR只能包含可析的字符数据,即非标记文本,但它不能包含自己的子元素。下面的YEAR元素是非法的,因为它包含了子元素:

<YEAR>

<MONTH>January</MONTH>

</YEAR>

1.3 子元素列表

为了声明LEAGUE元素必须有一个名称,只要声明LEAGUE_NAME元素,然后在LEAGUE声明后的括号内加入LEAGUE_NAME,如下面这样:

<!ELEMENT LEAGUE (LEAGUE_NAME)>

<!ELEMENT LEAGUE_NAME (#PCDATA)>

每个元素只能在其<!ELEMENT>内声明一次,即使它以其他<!ELEMENT>声明的子元素出现也一样。这里,我把LEAGUE_NAME声明放在引用它的LEAGUE声明之后,这没有关系。XML允许这一类提前引用。只要声明全部包含在DTD中,元素标记出现的顺序无关紧要。

1.4 序列

让我们限制一下SEASON元素。一个SEASON元素包含正好一个YEAR元素和其后的两个LEAGUE子元素。不把SEASON元素声明为可以包含ANY元素,我们在SEASON元素声明中包括这三个子元素,用括号括起来并用逗号分隔开,如下所示:

<!ELEMENT SEASON (YEAR, LEAGUE, LEAGUE)>

用逗号隔开的一系列子元素称为一个序列。利用这一声明,每个合法的SEASON元素必须包含正好一个YEAR元素,后面正好是两个LEAGUE元素,没有别的。整个文档类型定义现在看上去是下面的样子:

<!DOCTYPE SEASON [

<!ELEMENT YEAR (#PCDATA)>

<!ELEMENT LEAGUE (LEAGUE_NAME)>

<!ELEMENT LEAGUE_NAME (#PCDATA)>

<!ELEMENT SEASON (YEAR, LEAGUE, LEAGUE)>

]>

1.5 一个或多个子元素(+)

每个DIVISION有一个DIVISION_NAME和四到六个TEAM子元素。指定DIVISION_NAME很容易,方法如下:

<!ELEMENT DIVISION (DIVISION_NAME)>

<!ELEMENT DIVISION_NAME (#PCDATA)>

但是,TEAM子元素就很棘手。指明DIVISION元素有四个TEAM子元素很容易,如下所示:

<!ELEMENT DIVISION (DIVISION_NAME, TEAM, TEAM, TEAM, TEAM)>

五个和六个也不难。但是您怎样说明有四到六个TEAM子元素呢?实际上,XML没有提供实现的简单方法。但是可以在子元素清单的元素名后放一个加号(+)来说明有一个或多个子元素,例如:

<!ELEMENT DIVISION (DIVISION_NAME, TEAM+)>

这就是说一个DIVISION元素必须包含一个DIVISION_NAME子元素,后接一个或多个TEAM子元素。

1.6 零或多个子元素(*)

每个TEAM要包含一个TEAM_CITY,一个TEAM_NAME和不确定数目的PLAYER元素。因而,我们要指明一个TEAM元素可包含零或多个PLAYER子元素。在子元素清单中在元素名上附加一个星号(*)来实现这一目的。例如:

<!ELEMENT TEAM (TEAM_CITY, TEAM_NAME, PLAYER*)>

<!ELEMENT TEAM_CITY (#PCDATA)>

<!ELEMENT TEAM_NAME (#PCDATA)>

1.7 零或一个子元素(?)

需要给定类型的零个或一个元素。在子元素列表后面附加一个问号(?)可表明这一点,如下所示:

<!ELEMENT PLAYER (GIVEN_NAME, SURNAME, POSITION,

GAMES,GAMES_STARTED,AT_BATS?,RUNS?)

1.8 选择

假定子元素以一定的顺序出现或不出现。例如,在一项描述顾客购物的DTD中,结帐方式信息中的每项PAYMENT元素都有CREDIT_CARD子元素或CASH子元素以便提供付款方式的信息。然而,单独的PAYMENT元素不能同时使用两者。在父元素声明中,可以使用竖线(|)而不是逗号来分开子元素,以便指明文档作者需要输入一个或另一个子元素。例如,下面的语句就说明PAYMENT元素必须有CASH或CREDIT_CARD中的一个子元素。

<!ELEMENT PAYMENT (CASH | CREDIT_CARD)>

1.9 带括号的子元素

在父元素声明中,必须了解有关子元素安排的最后一件事是如何用括号分组元素。每一对括号把数个元素合为一个独立元素。括号内的元素可以作为独立元素嵌套在其他括号内。而且,还可以加上加号、逗号或问号后缀。您还可以将这些括号组合成更大的括号组合来构成复杂的结构。这是一项功能强大的技术。

例如,考虑一份由两个互相可交换的元素组成的清单。这基本上是HTML中定义清单的方法。每项<dt>标记要与一项<dd>标记相匹配。如果用XML来复制这一结构,dl元素的声明看起来是这样的:

<!ELEMENT dl (dt , dd)*>

括号表明要重复的是相互匹配的<dt><dd>元素对。

元素经常以或多或少的随机顺序出现。新闻杂志文章通常有一个标题,绝大多数后接文章段落,带有图形、照片、副栏、副标题、通篇夹杂的引文,也许在末尾还有作者行。可以在父元素声明中在括号内用竖线分组列出所有子元素来指明这些安排。然后您在括号外加星号来指明允许括号内元素出现零或多次。例如:

<!ELEMENT ARTICLE (TITLE, (P | PHOTO | GRAPH | SIDEBAR

| PULLQUOTE | SUBHEAD)*, BYLINE?)>

1.10 混合内容

读者可能已经注意到了,在以前的多数例子中,元素或者包含子元素,或者包含可析的字符数据,但不能同时包含两者。唯一的例外是以前例子中的一些基本元素。在这些例子中,全部标记的列表还没有完成。由于基本元素可以包含ANY数据,因而就既可以包含子元素又可以包含原始文本。

可以声明同时包含子元素和可析字符数据的标记。这就叫做混合内容。这样就可以给每个TEAM后面加上任意的文本块。例如:

<!ELEMENT TEAM (#PCDATA | TEAM_CITY | TEAM_NAME | PLAYER)*>

不能在包括#PCDATA的元素声明中使用逗号、问号或加号。用竖线分隔的元素和#PCDATA的列表是合法的。其他用法是不合法的。例如,下面的例子就不合法:

<!ELEMENT TEAM (TEAM_CITY, TEAM_NAME, PLAYER*, #PCDATA)>

1.11 空元素

合法的文档必须声明使用的空元素和非空元素。因为按定义,空元素没有子元素,声明很容易。可像通常一样使用包含空元素名的<!ELEMENT>来声明,但用关键词EMPTY (像所有XML标记一样区分大小写)代替了子元素的列表。例如:

<!ELEMENT BR EMPTY>

<!ELEMENT IMG EMPTY>

<!ELEMENT HR EMPTY>

使用了空标记的合法文档

<?xml version="1.0" standalone="yes"?>

<!DOCTYPE DOCUMENT [

<!ELEMENT DOCUMENT (TITLE,SIGNATURE)>

<!ELEMENT TITLE (#PCDATA)>

<!ELEMENT COPYRIGHT (#PCDATA)>

<!ELEMENT EMAIL (#PCDATA)>

<!ELEMENT BR EMPTY>

<!ELEMENT HR EMPTY>

<!ELEMENT LAST_MODIFIED (#PCDATA)>

<!ELEMENT SIGNATURE (HR, COPYRIGHT, BR, EMAIL,

BR, LAST_MODIFIED)>

]>

<DOCUMENT>

<TITLE>Empty Tags</TITLE>

<SIGNATURE>

<HR/>

<COPYRIGHT>1998 Elliotte Rusty Harold</COPYRIGHT><BR/>

<EMAIL>elharo@metalab.unc.edu</EMAIL><BR/>

<LAST_MODIFIED>Thursday,April 22,1999</LAST_MODIFIED>

</SIGNATURE>

</DOCUMENT>

  

2. 属性声明

如下例子,元素GREETING具有LANGUAGE属性,其属性值为ENGLISH

<GREETING LANGUAGE=”English”>

Hello XML!

</ GREETING>

元素可具有多个属性。结束标记不能带属性。与元素和实体相似,为保持文档的合法性,需要在文档的DTD中声明属性。<!ATTLIST>标记用于声明属性,其形式如下:

<!ATTLIST Element_name Attribute_name Type Default_value>

Element_name为拥有该属性的元素名。Attribute_name为属性名,Type为下表列出的10种有效属性类型的一种。最常用的属性类型为CDATA。最后,若未规定属性值,则属性值为Default_value

表 属性类型

类 型

含 义

CDATA

字符数据不是标记的文本

Enumerated

可能取值的列表,可从中选出正确的值

ID

不能被文档中其他任何ID类型属性共享的数字,具有唯一性

IDREF

文档中元素的ID类型属性的值

IDREFS

由空格分开的若干个ID

ENTITY

DTD中声明的实体名

ENTITIES

DTD中声明的若干个实体的名字,彼此间由空格分开

NMTOKEN

XML名称

NOTATION

DTD中声明的注释名

NMTOKENS

由空格分开的多个XML名称

例如,研究下列元素:

<GREETING LANGUAGE= "Spanish">

Hola!

</GREETING>

DTD中,可按如下格式声明该元素:

<!ELEMENT GREETING (#PCDATA)>

<!ATTLIST GREETING LANGUAGE CDATA "English">


3. DTD中的注释

像一份XML文档的其他部分一样,DTD也可以包含注释。这些注释不能在声明中出现,但可以在声明外出现。注释通常用来组织不同部分的DTD,为一些元素的许可内容提供说明,并对元素作进一步的解释。例如,YEAR元素的声明可以有这样的注释:

<!--A four digit year like 1998, 1999, or 2000 ?-->

<!ELEMENT YEAR (#PCDATA)>

像所有注释一样,这只是为了便于人们阅读源代码,XML处理程序会忽略注释部分。

 

4. 在文档间共享通用的DTD

当使用外部DTD时,文档类型声明要加以改变。DTD不再是包括在方括号中,而是在SYSTEM关键词后接一个能找到DTD的绝对或相对的URL。例如:

<!DOCTYPE root_element_name SYSTEM "DTD_ URL">

这里root_element_name像以前一样是基本元素的名称,SYSTEM是一个XML关键词,DTD_URL是能找到DTD的绝对或相对的URL。例如:

<!DOCTYPE SEASON SYSTEM "baseball.dtd">

DTD是起始于<!DOCTYPE root_element_name [终止于]>之间的所有内容。但不包括<!DOCTYPE root_element_name [和]>。可以将其保存在扩展名为.dtd的文件中,文档名并不重要。如1.11中的清单的DTD如下,假定名为list111.dtd:

<!ELEMENT DOCUMENT (TITLE,SIGNATURE)>

<!ELEMENT TITLE (#PCDATA)>

<!ELEMENT COPYRIGHT (#PCDATA)>

<!ELEMENT EMAIL (#PCDATA)>

<!ELEMENT BR EMPTY>

<!ELEMENT HR EMPTY>

<!ELEMENT LAST_MODIFIED (#PCDATA)>

<!ELEMENT SIGNATURE (HR, COPYRIGHT, BR, EMAIL,

BR, LAST_MODIFIED)>

接下来,需要改动文档本身。因为要依赖于另一文档中的DTD,XML声明不再是独立的文档。所以standalone属性要改为no,如下所示:

<?xml version="1.0" standalone="no"?>

然后还要改变<!DOCTYPE>标记,借助于包括SYSTEM关键字和URL(通常是相对的)使它指向DTD。

<!DOCTYPE DOCUMENT SYSTEM "list111.dtd" >

文档的其余部分与以前相同。但是,现在序言部分只包含XML声明和文档类型声明而不包括DTD。

 

5 公共的DTD

关键词SYSTEM是为单个作者或小组所用的私有的DTD使用的。但作为XML承诺的一部分,可令覆盖整个产业的广泛组织(如ISO或IEEE)能够将公共DTD加以标准化,以便用于各自的专门领域。这样的标准化可以让人们不用为同一项目重复作标记,并且使用户共享公用文档更容易。

为创建组织之外的编写者设计的DTD使用PUBLIC关键词而不使用SYSTEM关键词。并且DTD有一个文件名。句法如下:

<!DOCTYPE root_element_name PUBLIC "DTD_name" "DTD_URL">

root_element_name仍然是基本元素名称。PUBLIC是XML关键词,说明这一DTD是公共使用并具有名称。DTD_name是与此DTD联系的名称。有些XML处理程序会使用名称从中心库中检索DTD。最后,如果DTD不能根据名称从熟知的库中检索到,则DTD_URL是一个能找到该DTD的相对或绝对URL。

如果一项DTD是ISO标准,它的名称要以字符串“ISO”开始。如果是非ISO标准组织批准的DTD,它的名称以加号(+)开始。如果不是标准组织批准的DTD,它的名称以连字符 (-)开始。这些开始字符串后接双斜线(//) 和DTD所有者的名字,其后接另一个双斜线和DTD描述的文档类型,然后又是一个双斜线后接ISO639语言标识符,如EN表示英语。在http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt处列有完整的ISO639标识符。

读者也许注意到了许多HTML编辑器如BBEdit会在其创建的每个HTML文档开端放入下列字符串:

<!DOCTYPE HTML PUBLIC"-//W3C//DTD HTML//EN">

现在可能了解这些字符串是什么意思了!它表明文档符合一项非标准 (-) 的HTML的DTD,由W3C用英语制作。


6 在程序中使用DTD验证XML

a. 定义DTD如下:

<!ELEMENT config (datasource*)>
<!ELEMENT datasource (name, driver, url, user, pwd, createtime)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT driver (#PCDATA)>
<!ELEMENT url (#PCDATA)>
<!ELEMENT user (#PCDATA)>
<!ELEMENT pwd (#PCDATA)>
<!ELEMENT createtime (#PCDATA)>
b. 在XML中声明引用的DTD:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE config SYSTEM "config.dtd">
<config>
<datasource>
<name>mysql</name>
<driver>org.gjt.mm.mysql.Driver</driver>
<url>jdbc:mysql://localhost:3306/mysql?useUnicode=true&characterEncoding=GB2312</url>
<user>root</user>
<pwd>123</pwd>
<createtime>1149184098392</createtime>
</datasource>
<datasource>
<name>oracle</name>
<driver>oracle.jdbc.driver.OracleDriver</driver>
<url>jdbc:oracle:thin:@localhost:1521:cqu</url>
<user>system</user>
<pwd>manager</pwd>
<createtime>1149226769500</createtime>
</datasource>
<datasource>
<name>sqlserver</name>
<driver>sun.jdbc.odbc.JdbcOdbcDriver</driver>
<url>jdbc:odbc:Northwind</url>
<user>sa</user>
<pwd>123</pwd>
<createtime>1150866768890</createtime>
</datasource>
</config>

c.程序的写法:
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
// 默认为false,表示不验证
System.out.println("default validate: " + docBuilderFactory.isValidating());
// 设置为true
docBuilderFactory.setValidating(true);
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
// 在调用parse()时,如果载入的xml文件中声明了DTD,那么将进行合法性验证。
// 如果不合法,将抛出异常,可以通过setErrorHandler(ErrorHandler eh)来进行相应处理。
Document srcDoc = docBuilder.parse(xmlFilePath);
 

 

 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值