1、元素类型声明
元素类型声明不但说明了每个文档中可能存在的元素,给出了元素的名称,而且给出了元素的具体类型。一个XML元素可以为空,也可以只包含字符数据,还可以有若干个子元素,而这些子元素同时又可以有它们的子元素。
元素的类型声明采用如下的语法格式:
<!ELEMENT 元素名称 元素内容说明>
元素内容说明可以指明五种可能的元素内容形式:#PCDATA、子元素、混合内容、EMPTY和ANY。
1.1 #PCDATA
关键字#PCDATA说明元素包含字符数据。
例如:
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE hr [ <!ELEMENT hr (#PCDATA)> ]> <hr>人力资源标准</hr>1.2 子元素
当一个元素只包含子元素,而没有字符数据时,则称此元素类型具有元素型内容(element content)。在该类型的元素声明时,通过内容模型来指定在其内容上的约束。内容模型是决定子元素类型和子元素出现顺序的一种简单语法。
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE company [ <!ELEMENT company (employe)> <!ELEMENT employe (name,age,sex,salary,interest*,spouse?)> <!ELEMENT name (#PCDATA)> <!ELEMENT age (#PCDATA)> <!ELEMENT sex (#PCDATA)> <!ELEMENT salary (cash | credit_card)> <!ELEMENT cash (#PCDATA)> <!ELEMENT credit_card (#PCDATA)> <!ELEMENT interest (#PCDATA)> <!ELEMENT spouse (#PCDATA)> ]> <company> <employe> <name>张三</name> <age>19</age> <sex>男</sex> <salary> <cash>2000</cash> </salary> </employe> </company>以逗号隔开的多个元素称为一个序列表示,这些元素在文档中必须严格按照这个顺序出现。
用竖线隔开的多个元素称为选择,值元素必须有其中之一。
例子中的interest后面的星号( * )的意思是0个或多个,如果是加号( + )指的是一个或多个,如果是问号( ? )表示零个或一个。
利用括号、逗号、竖线、星号、加号、问号的组合,可以说明很复杂的内容模型。例如:
<!ELEMENT 简历 (名字,性别,年龄,(电话|手机),家庭住址?,兴趣爱好*,教育经历+,工作经验*)>
内容模型的规则虽然简单,但是可以产生灵活多样的结构。
1.3 混合内容
混合内容说明元素既可以包含字符元素,也可以包含子元素。混合内容必须被定义为零个或多个。
例如:
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE employee[ <!ELEMENT employee(#PCDATA | name)*> <!ELEMENT name(#PCDATA)> ]> <employee> 员工信息 <name>张三</name> </employee>在使用混合内容模型时,#PCDATA关键字必须是模型中的第一个选项,不能再模型中使用逗号、问号和加号。用竖线分隔的#PCDATA和元素的列表是合法的,其它用法都是不合法的。
1.4 EMPTY
关键字EMPTY表明该元素既不包含字符数据,也不包含子元素,是一个空元素。如果在该文档中,元素本身已经表示了明确的含义,就可以在DTD中用关键字EMPTY来声明元素。例如:
<!ELEMENT br EMPTY> 表明br是一个没有内容的空元素。1.5 ANY
关键字ANY表明该元素可以包含任何的字符数据和子元素,只要它们不违反XML格式良好的约束就可以了。例如:
<!ELEMENT employee ANY> 表明employee可以包含任何形式的内容。在实际使用时,应该尽量避免使用ANY,一个定义明确的DTD有助于我们理清文档的结构,更好地理解文档。
2、实体声明
有可能在多个文档中调用同样的内容,如公司名称、版权声明等,为了避免重复输入这些内容,我们可以声明一个实体来表示这个实体,在文档中只需要引用这个实体。在XML处理器对这个文档进行分析处理时,引用实体的的位置会被实体的内容所替换。
有两种类型的实体:一般实体(general entity)和参数实体(parameter entity)。一般实体是在文档中使用的实体,而参数实体则是在DTD中使用的已分析实体。不管是一般实体,还是参数实体,都是用ENTITY关键字来声明。
2.1 一般实体
一般实体的声明语法如下:
<!ENTITY 实体名 “实体内容”>引用实体的方式为:“&实体名;”
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE website [ <!ELEMENT website (name,copyright)> <!ELEMENT name (#PCDATA)> <!ELEMENT copyright (#PCDATA)> <!ENTITY name ”鹿放青崖的空间”> <!ENTITY copyright ”© 2005,鹿放青崖的空间。All Right Reserved.”> ]> <website> <name>&name;</name> <copyright>©right;</copyright> </website>2.2 参数实体
参数实体只能在DTD中使用,它的声明如法如下:
<!ENTITY % 实体名 “实体内容”> 注意在声明时,ENTITY、%和实体名之间各有一个空格。实体的引用方式为:“%实体名;”。 参数实体引用的例子: <?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE website [ <!ELEMENT website (name,copyright)> <!ELEMENT name (#PCDATA)> <!ELEMENT copyright (#PCDATA)> <!ENTITY % name ”鹿放青崖的空间”> <!ENTITY % copyright ”© 2005,%name; All Right Reserved.”> ]> <website> <name>鹿放青崖的空间</name> <copyright>©right;</copyright> </website> ©表示版权符号( © )以上例子是不合法的,因为XML不允许在内部DTD标记声明的内部出现对参数实体的引用。对于外部的DTD子集中,在标记声明的内部允许出现对参数实体的应用。
为了改正将以上的DTD声明放到一个外部的DTD文件中,这里命名为website.dtd,内容如下:
<?xml version=”1.0” encoding=”gb2312”?> <!ELEMENT website (name,copyright)> <!ELEMENT name (#PCDATA)> <!ELEMENT copyright (#PCDATA)> <!ENTITY % name ”鹿放青崖的空间”> <!ENTITY % copyright ”© 2005,%name; All Right Reserved.”>然后我们在XML文档中,给出一个文档类型声明,使用一个SYstem关键字去引用一个私有的外部DTD文件:
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE website SYstem “website.dtd”> <website> <name>鹿放青崖的空间</name> <copyright>©right;</copyright> </website>如果不是在标记声明的内部,那么即使在内部DTD中的其它地方(比如在标记可以出现的地方)是可以引用参数实体的,例子如下:
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE website [ <!ELEMENT website (name,copyright)> <!ELEMENT name (#PCDATA)> <!ELEMENT % cprt “<!ELEMENT copyright (#PCDATA)>”> %cprt; <!ENTITY name ”鹿放青崖的空间”> <!ENTITY copyright ”© 2005,鹿放青崖的空间; All Right Reserved.”> ]> <website> <name>&name</name> <copyright>©right;</copyright> </website>注意:在内部DTD子集当中,参数实体的引用而不能在标记声明内部出现,可以在标记声明允许出现的地方出现。对于外部DTD子集,则没有这个限制。
在DTD中,所有的参数实体必须在引用之前进行声明。这意味着内部DTD子集不能引用外部DTD中声明的实体,这是因为XML处理器将首先读取内部子集,也就是说,内部子集中的实体和属性表声明的优先级要比在外部子集中的高。
2.3 内部实体和外部实体
内部实体在XML文档内部定义,实体的内容在声明中给出,内部实体都是已分析的实体,它们没有单独的物理存储对象。以上介绍的一般实体和参数实体都属于内部实体,可以称为内部一般实体和内部参数实体。
2.4 外部实体
外部实体在单独的(外部) 文件中定义,外部实体可以是已分析实体,也可以是未分析实体,外部一般实体的声明形式如下:
<!ENTITY copyright SYstem “http://www.lfqy.org/copyright.xml”>关键字SYstem表明这是一个私有的外部一般实体,后面的URI称为该实体的系统标识符,用于给出外部文件的位置。
copyright.xml文件的内容为:
<?xml version=”1.0” encoding=”gb2312”?> ©2004,鹿放青崖的空间,All Rights Reserved.<?xml version=”1.0” encoding=”gb2312”?>称为文本声明,文本声明类似于XML声明,不过文本声明没有standalone属性,并且version属性也是可选的,外部已分析实体可以使用不同于UTF-8的编码,使用文本声明来指明实体内容的编码方式。
每个外部已分析实体都应该以文本声明开始,文本声明只能在外部已分析实体的开头出现,不能出现在其他任何位置。在外部已分析实体中的文本声明不会作为替换文本的一部分而出现。
例:
<?xml version=”1.0” encoding=”gb2312”?> ©2004,鹿放青崖的空间,All Rights Reserved.将以下内容保存到文件copyright.xml,在以下的xml文件中可以对这个外部实体进行引用:
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE website [ <!ELEMENT website (name,copyright)> <!ELEMENT name (#PCDATA)> <!ELEMENT % cprt “<!ELEMENT copyright (#PCDATA)>”> %cprt; <!ENTITY name ”鹿放青崖的空间”> <!ENTITY copyright SYstem “copyright.xml”> ]> <website> <name>&name</name> <copyright>©right;</copyright> </website>也可以使用PUBLIC关键字来声明公共的外部一般实体,以声明形式和使用了关键字PUBLIC的外部DTD声明类似,如下:
<!ENTITY open-hatch PUBLIC “-//Textuality//TEXT Standard open-hatch boilerplate//EN” “http://www.textuality.com/boilerplate/OpenHatch.xml”>“-//Textuality//TEXT Standard open-hatch boilerplate//EN”称为该实体的公共标识符,后面的URI部分为该实体的系统标识符。下面的例子声明了一个外部一般未分析实体。
<!ENTITY hatch-pic SYstem “../grafix/OpenHatch.gif” NDATA gif>与一般实体类似,参数实体的替换文本也可以位于外部文件中,其声明形式和一般实体类似。
3、属性表声明
属性用于将名字-值对与元素进行关联,属性说明只能在开始标签和空元素标签中出现,属性表声明详细说明了与给定元素类型相关联的每一个属性的名字、数据类型和缺省值(如果有的话)。
属性表声明的语法如下:
<!ATTLIST 元素名 属性名 属性类型 缺省声明>元素名是属性所属的元素的名字,属性名是属性的命名,属性类型则用来指定该属性是属于哪种类型,共有十种类型,缺省声明用于说明在钙元素中该属性是否必须出现,如果不是必须出现,那么当该属性没有出现时,XML处理器应该如何处理。
3.1 缺省声明
缺省声明可以有四种缺省设置,#required、#IMPLIED、#FIXED+缺省值、只有缺省值。
3.1.1 #required
#required关键字说明必须为该元素提供该属性。
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE bbs [ <!ELEMENT bbs (article*)> <!ELEMENT article (title,author)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> 以下是采用ATTLIST关键字进行属性表声明。 <!ATTLIST author ip CDATA #required> ]> <bbs> <article> <title>关于属性表声明的问题</title> <author ip “192.168.1.1”>张三</author> </article> <article> <title>关于XML应用的问题</title> <author ip “192.168.1.1”>李四</author> </article> </bbs>3.1.2 #IMPLIED
#IMPLIED关键字说明元素可以包含该属性也可以不包含该属性。
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE bbs [ <!ELEMENT bbs (article*)> <!ELEMENT article (title,author)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> 以下是采用ATTLIST关键字进行属性表声明。 <!ATTLIST author ip CDATA #IMPLIED > ]> <bbs> <article> <title>关于属性表声明的问题</title> <author ip “192.168.1.1”>张三</author> </article> <article> <title>关于XML应用的问题</title> <author >李四</author> </article> </bbs>3.1.3 #FIXED+缺省值
#FIXED+缺省值,说明一个固定的属性缺省值,文档的编写者不能修改该属性的值,如果元素中不包含这个属性,XML处理器将以声明的缺省值向应用程序报告该属性。
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE bbs [ <!ELEMENT bbs (article*)> <!ELEMENT article (title,author)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> 以下是采用ATTLIST关键字进行属性表声明。 <!ATTLIST author ip CDATA #IMPLIED > <!ATTLIST article copyright CDATA #IMPIED “版权归本论坛所有”> ]> <bbs> <article> <title>关于属性表声明的问题</title> <author ip “192.168.1.1”>张三</author> </article> <article> <title>关于XML应用的问题</title> <author >李四</author> </article> </bbs>3.1.4只有缺省值
与FIXED+缺省值一样,如果元素不包含该属性,XML处理器将以声明的缺省值向应用程序报告该属性,不同的是这种声明方式属性的值是可以改变的。
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE bbs [ <!ELEMENT bbs (article*)> <!ELEMENT article (title,author)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> 以下是采用ATTLIST关键字进行属性表声明。 <!ATTLIST author ip CDATA #IMPLIED > <!ATTLIST article copyright CDATA #IMPIED “版权归本论坛所有”> <!ATTLIST article style CDATA “txt”> ]> <bbs> <article> <title>关于属性表声明的问题</title> <author ip “192.168.1.1”>张三</author> </article> <article style “html”> <title>关于XML应用的问题</title> <author >李四</author> </article> </bbs>也可以将过个属性声明合并成一个,上面的例子可以改为:
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE bbs [ <!ELEMENT bbs (article*)> <!ELEMENT article (title,author)> <!ELEMENT title (#PCDATA)> <!ELEMENT author (#PCDATA)> 以下是采用ATTLIST关键字进行属性表声明。 <!ATTLIST author ip CDATA #IMPLIED > <!ATTLIST article copyright CDATA #IMPIED “版权归本论坛所有” style CDATA “txt”> ]> <bbs> <article> <title>关于属性表声明的问题</title> <author ip “192.168.1.1”>张三</author> </article> <article style “html”> <title>关于XML应用的问题</title> <author >李四</author> </article> </bbs>4 属性类型
在属性表声明时,总共有十种属性类型可以选择,分别是:CDATA、Enumerated、ID、IDREF、IDREFS、ENTITY、ENTITIES、NMTOKEN、NMTOKENS、NOTATION。
4.1 CDATA
CDATA是最常用的一种属性类型,表明属性值为字符数据,与元素内容说明中的#PCDATA相同,如果属性值需要出现小于号( < )和双引号( “ ),可以通过预定义实体引用或字符引用的方式插入小于号和双引号。如果包含的和号( & )不是字符或者实体引用的起始定界符,也必须使用预定义实体引用或者字符引用的方式插入。
4.2 Enumerated
在声明属性时,可以限制属性的取值只能从一个列表中选择,这类属性属于枚举类型。枚举类型的属性有时候是很有用的,例如:person元素有一个sex属性,我们希望这个属性的取值只能是male或female,在声明属性时,将这两个值放到圆括号中,并用竖线( | )分隔,如下所示:
<!ATTLIST person sex (male|female) #required>
列表中的可选属性值,不用加双引号( “ )或单引号( ‘ ),但是在给属性赋值时,需要带上双引号或单引号。另外要注意的是,再给属性赋值时,不仅必须使用枚举类型声明中的可选值,而且还要注意属性值的大小写,Male、Female、MALE等都是无效的。
4.3 ID、IDREF、IDREFS
一个ID类型的属性值唯一标识XML文档中的一个元素。一个ID类型的属性值必须遵守XML名称定义的规则,以字母、下划线或冒号开头,名称中可以包含字母、数字、下划线以及其它在XML标准中允许的字符,名称中不能带有空格。一个元素只能有一个ID类型的属性,ID类型的属性必须设置为#IMPLIED或者#required,因为ID类型属性的每一个取值都是用来标识一个特定的元素,为ID类型的属性提供缺省值,特别是固定的缺省值是毫无意义的。
例:
在一个公司中通常用员工编号来标识一个员工。
<?XML version=”1.0” encoding=”gb2312”> <!DOCTYPE company[ <!ELEMENT company (employee*)> <!ELEMENT employee (name)> <!ELEMENT name (#CDATA)> <!ATTLIST employee sn ID #REQUIED> ]> <company> <employee sn=”E-200402100001”> <name>张三</name> </employee> <company> <company> <employee sn=”E-200402100110”> <name>李四</name> </employee> <company>IDREF类型的属性值为同一文档中另一个元素的ID类型的属性值,而这另一个元素的ID类型的属性值必须是已经存在的。利用ID和IDREF这两种类型的属性,我们可以在两个对象之间建立一种关联关系。
例:
<?XML version=”1.0” encoding=”gb2312”> <!DOCTYPE company[ <!ELEMENT company (employee|manager) *> <!ELEMENT employee (name)> <!ELEMENT manager EMPTY> <!ELEMENT name (#CDATA)> <!ATTLIST employee sn ID #REQUIED> <!ATTLIST manager mgrid IDREF #REQUIED> ]> <company> <employee sn=”E-200402100001”> <name>张三</name> </employee> <employee sn=”E-200402100110”> <name>李四</name> </employee> <manager mgrid=”E-200402100001”> </company>以上文档也可以改成以下形式:
<?XML version=”1.0” encoding=”gb2312”> <!DOCTYPE company[ <!ELEMENT company (employee*) > <!ELEMENT employee (name)> <!ELEMENT name (#CDATA)> <!ATTLIST employee sn ID #REQUIED> <!ATTLIST employee manager IDREF #IMPLIED>意思是可有可无 ]> <company> <employee sn=”E-200402100001”> <name>张三</name> </employee> <employee sn=”E-200402100110” manager=”E-200402100001”> <name>李四</name> </employee> <employee sn=”E-200402100111” manager=”E-200402100001”> <name>王五</name> </employee> </company>如果一个属性需要引用文档中多个ID类型的属性值,那么可以把它声明为具有IDREFS类型。IDREFS类型的属性值是一系列以空格分隔的ID类型的属性值,而且必须与文档中已有的ID类型属性值相匹配。
<?XML version=”1.0” encoding=”gb2312”> <!DOCTYPE library[ <!ELEMENT library (books,records) > <!ELEMENT books (book+)> <!ELEMENT book (#CDATA)> <!ELEMENT records (term*)> <!ELEMENT term (date,person)> <!ELEMENT date (#CDATA)> <!ELEMENT person EMPTY> <!ATTLIST book bookid ID #REQUIED> <!ATTLIST person name CDATA #REQUIED> <!ATTLIST book borrowed IDREFS #REQUIED> <!ATTLIST employee manager IDREF #IMPLIED>意思是可有可无 ]> <library> <books> <book bookid=”b-1-1”>XML详解</book> <book bookid=”b-1-2”>Servlet从入门到精通</book> <book bookid=”b-1-3”>JSP实例编程</book> </books> <records> <term> <date>2004-3-14<date> <person name=”张三” borrowed=” b-1-1 b-1-2”> <term> <term> <date>2004-5-15<date> <person name=”李四” borrowed=” b-1-1 b-1-2 b-1-3”> <term> </records> </library>4.4 ENTITY、ENTITIES
ENTITY类型的属性把外部的二进制数据链接到文档。ENTITY类型的属性值是在外部DTD中声明的未分析的一般实体的名称。
例如,我想在文档中包含一副外部的图像,可以声明一个ENTITY类型的属性来引入图像,如下:
<!ATTLIST image src ENTITY #required>在DTD中,还需要声明一个外部的一般实体:
<ENTITY logo SYstem “http://www.lfqy.org/logo.gif” NDATA gif>关键字NDATA表示该实体是一般未分析的实体,后面的gif是记号名称(notation name),说明实体的数据格式或指定一个外部的处理程序。记号为gif的声明如下:
<!NOTATION gif SYstem “iexplore.exe”>在XML文档中,可以在src属性中引用图像:
<image src=”logo”/>这句代码将http://www.lfqy.org/logo.gif文件与image元素关联在一起。
ENTITIES类型和IDREFS类型的使用是类似的,它的值是多个以空格分隔的ENTITY类型的属性值。
例如我们可以再添加一个实体声明(外部未分析的一般实体)
<!ENTITY banner SYstem “http://www.lfqy.com.cn/banner.gif” NDATA gif>然后将元素image的属性声明改为:
<!ATTLIST image src ENTITIES #required>在XML文档中,通过src属性引用两幅图像:
<image src=”logo banner”/>4.5 NMTOKEN、NMTOKENS、NOTATION
4.5.1 NMTOKEN、NMTOKENS
NMTOKEN(name token),名称标记是任何命名字符的混合体。NMTOKEN类型的属性值是受限制的文本,只能包含名称字符,不能包含空白字符。XML名称不能以除字母、下划线和冒号之外的其它字符开头,而名称标记没有这个限制。所有的XML名称都是名称标记,但不是所有的名称标记都是XML名称。
为了限制文件名属性的取值不能有空格,我们可以采取如下的属性声明方式:
<!ATTLIST file name NMTOKEN #required>在文档中,可以按如下方式使用name属性:
<file name=”XML讲座.doc”>因为name属性的类型是NMTOKEN,所以在用文件名给name属性赋值的时候,在文件名中不能包含有空格。
NMTOKENS类型与IDREFS和ENTITIES类似,它的值有多个名称标记构成,每个名称必须是有效的名称标记,它们之间以空格分隔。例如在DTD中声明:
<!ATTLIST files name NMTOKENS #required>在文档中使用:
<file name=”XML讲座.doc JSP讲座.doc”/>有时候,你可能会用NMTOKEN类型的属性来让用户输入特定的值,但是要注意的是:在使用NMTOKEN类型的属性时,其值是否有效,需要文档的作者自己去保证,XML处理器只能确保名称是合法的(也就是检查名称中有没有空格),而不会检查值的有效性。
4.5.2 NOTATION
NOTATION类型属性的值就是在记号声明中的名称。在现实中,有很多数据都是无法用XML来表示的,例如:声音、图像、影像等对于这些数据,XML处理器通常都不支持。通过DTD中的记号声明(notation declaration),为非XML数据描述一种可能的格式,或者指定一个外部的处理程序。
记号声明有两种形式,一种是使用MIME类型,形式如下:
<!NOTATION gif SYstem “image/gif”>另一种是使用URI路径,指出外部处理程序的位置,如下:
<!NOTATION gif SYstem “iexplore.exe”>对于记号的声明,也可以使用PUBLIC关键字来代替SYstem关键字,并添加公共的名称和URI(用法类似于使用了关键字PUBLIC的外部DTD声明)。
使用实体类型的属性可以将gif图像文件与元素相关联,然而XML处理器通常都不能处理二进制格式的数据。通过使用记号声明可以说明连接到XML文档的外部数据项的格式或者指定相关的外部处理器。对XML文档那个进行处理的应用程序可以从XML处理器中得到外部未分析的实体和相应的记号的有关信息,然后决定对该实体的数据的处理方式,例如对可识别的图像格式直接显示或者调用记号声明中指出的外部程序对实体数据进行处理。在实际应用中,对未分析实体的处理,总是由特定的应用程序来完成。XML处理器本身只是向应用程序报告未分析实体和记号的信息。
例:
<?xml version=”1.0” encoding=”gb2312”?> <!DOCTYPE webpage[ <!ELEMENT webpage (image)> <!ELEMENT image (src)> <!ELEMENT src (#PCDATA)> <!NOTATION gif SYstem “image/gif”> <!NOTATION jpg SYstem “explore.exe”> <!ATTLIST image type NOTATION (gif|jpg) #required> ]> <webpage> <image type=”jpg”> <src>http://www.lfqy.org/images/photo.jpg</src> </image> </webpage>