3.6 定义元素属性
元素除了可以包含子元素之外,还可以添加属性,为XML元素添加属性的格式如下:
DTD也为XML元素添加属性提供了支持。由于XML文档中元素是一等公民,而属性不能独立存在,因此定义属性时必须指定它属于哪个元素。DTD可以定义属性所属的元素、属性类型和属性必须遵守的规则等。DTD中定义属性的语法格式如下:
在上面的语法中,“元素对属性的约束”和“默认值”两个部分是可选的,对于它们的使用有如下几种情况:
在没有指定“元素对属性的约束”时,必须为该属性指定“默认值”。
当“元素对属性的约束”是#REQUIRED时,不能为该属性指定“默认值”。
当“元素对属性的约束”是#IMPLIED时,不能为该属性指定“默认值”。
当“元素对属性的约束”是#FIXED时,必须为该属性指定“默认值”。
3.6.1 对属性的约束规则
由前面的介绍可以看出,每个<!ATTLIST…>定义一个属性。实际的XML文档中可能包括多个属性,因此DTD中需要使用多个<!ATTLIST…>定义属性。元素对属性的约束规则有如下几种:
#REQUIRED:必需的属性,意味着必须为该元素提供该属性。
#IMPLIED:该属性是可有可无的。
#FIXED:该属性的值是固定的,定义时必须指定固定值。使用该元素时无须为其分配该属性,XML处理器将自动为该属性增加固定值。
对于#REQUIRED属性,只要使用对应元素,就必须给出相应的属性。对于下面的DTD定义:
程序清单:codes\03\3.6\required.dtd
上面的DTD为<作者…/>元素定义了“地址”属性,并指定了对该属性的约束是#REQUIRED,这表明使用<作者…/>元素时必须指定“地址”属性。
程序清单:codes\03\3.6\required.xml
上面的XML文档中使用<作者…/>元素时指定了“地址”属性,因此该XML文档是有效的,如果删除了该元素里的“地址”属性,则该XML文档将变成无效XML文档。
如果想定义某个属性是可有可无的,可使用#IMPLIED,或者干脆不指定任何约束规则。如果没有指定任何约束规则,则定义该属性时应该为其指定默认值。
如果使用#FIXED定义某个属性,则表明该属性只能固定地使用默认值,因此使用#FIXED约束规则时必须为属性指定默认值。
使用#FIXED约束定义某个属性,表明使用该元素时可以不再为该属性指定属性值。对于下面的DTD定义:
程序清单:codes\03\3.6\fixed.dtd
在上面的DTD中定义了“地址”属性,该属性属于<作者…/>元素。该属性被定义了#FIXED约束规则,即它是个具有固定值的属性,其值为“广州”。这意味着使用<作者…/>元素时,可以不指定“地址”属性。如果不提供“地址”属性,XML解析器将自动为<作者…/>元素增加“地址”属性,且其属性值为“广州”;也可以为<作者…/>元素指定“地址”属性,但指定“地址”属性时,其值必须是“广州”。
例如下面的文档是无效的:
程序清单:codes\03\3.6\fixed.xml
上面的XML文档里第2个<计算机书籍…/>元素的<作者…/>子元素指定了“地址”属性,但它的属性值与DTD中定义的固定值不一致,因而是个无效的XML文档。如果将上面的文档中的“上海”改为“广州”,则该文档将变为有效的XML文档。
3.6.2 定义属性类型
从上面的定义可以看出,DTD不仅可以定义一个元素包括的属性,以及元素对属性的约束,还可以使用CDATA定义元素类型,这意味着DTD还可以定义属性能接受哪些合法值。
DTD支持的属性类型如表3.1所示。
表3.1 DTD支持的属性类型
3.6.2.1 枚举类型
上面在介绍属性类型时,除了枚举之外都有一个专用的属性类型,但枚举类型则通过在括号里列出各个枚举值来实现,看下面的DTD:
程序清单:codes\03\3.6\enumerated.dtd
该DTD定义了3个元素:<购物车…/>、<肉…/>和<水果…/>,使用<肉…/>元素必须指定“类型”属性,该属性是个枚举类型,其值只能是“鸡肉”、“牛肉”、“猪肉”和“鱼肉”之一。使用<水果…/>元素时也必须指定“类型”属性,其值只能是“苹果”、“梨”、“桔子”之一。
根据上面的DTD定义的规则,下面的XML文档是有效的:
程序清单:codes\03\3.6\enumerated.xml
3.6.2.2 ID、IDREF、IDREFS类型
ID类型是一种较为严格的约束,它要求属性值必须是有效的XML标识名,而且在整个XML文档中其值不能重复。
程序清单:codes\03\3.6\idandidref.dtd
上述DTD定义了<bean…/>、<object…/>元素都可指定id属性,该id属性必须是ID类型的;<property…/>元素里可以指定ref和set属性,其中ref的属性值必须是一个已存在的ID类型的属性值,而set的属性值则必须是多个已存在的ID类型的属性值。如下所示的XML文档是有效的:
程序清单:codes\03\3.6\idandidref.xml
上面的XML文档中所有<bean…/>、<object…/>元素的id属性值都是唯一的,这就满足了ID类型的要求。除此之外,<property…/>元素的ref属性值是另一个已经存在的ID类型的属性值,set属性值是多个已经存在的ID类型的属性值,因此上面的XML文档是有效的。
3.6.2.3 NMTOKEN和NMTOKENS类型
NMTOKEN是一个比ID类型更宽松的类型,它只要求该属性的属性值是个合法的XML标识即可。也就是说,NMTOKEN类型的属性值也是字符串数据,但NMTOKEN约束比CDATA约束要严格,即NMTOKEN类型的属性值中能出现的字符要少。关于CDATA和NMTOKEN的区别,请看下面两份DTD文件。
使用CDATA定义的DTD文档:
程序清单:codes\03\3.6\cdata.dtd
下面是使用NMTOKEN定义的DTD文档:
程序清单:codes\03\3.6\nmtoken.dtd
上面两份DTD仅仅是定义“地址”属性的类型有区别,第1份指定了“地址”属性是CDATA类型,第2份则指定了“地址”属性是NMTOKEN类型。下面的XML文档对于第1份DTD文档是正确的,对于第2份DTD文档则是不正确的。
程序清单:codes\03\3.6\cdata.xml
上面的XML文档使用cdata.dtd作为语义约束是有效的XML文档,因为其中<作者…/>元素里的“地址”属性值只是一个字符串数据;但如果将上面的XML文档改为使用nmtoken.dtd作为语义约束,则不再是有效的XML文档,因为NMTOKEN类型的属性值里不能包含#符号。
关于ENTITY、ENTITIES和NOTATION类型的属性值在3.8节有更详细的介绍。