3.5 定义子元素
元素包含子元素是XML文档中最常见的情形,DTD可以有效地定义各元素之间的父子关系,从而有效地描述整个文档的结构。定义元素包含的子元素时,各子元素之间存在几种逻辑关系,下面依次介绍这几种逻辑关系。
3.5.1 有序的子元素
如果使用英文逗号(,)作为子元素之间的分隔符,则子元素之间必须遵守所定义的顺序。也就是说,英文逗号(,)用于分隔有序的子元素。对于下面的DTD文件:
程序清单:codes\03\3.5\sequence.dtd
上面的DTD在定义<计算机书籍…/>元素的子元素时,各子元素之间以英文逗号(,)分隔,这表明<书名…/>、<价格…/>、<作者…/>和<简要介绍…/>4个子元素有严格的顺序要求,必须遵守上面定义的顺序。因此下面的XML文档才是有效文档:
程序清单:codes\03\3.5\sequence.xml
上述XML文档中<计算机书籍…/>元素里包含的<书名…/>、<价格…/>、<作者…/>和<简要介绍…/>4个子元素的顺序与DTD中定义的顺序完全匹配,因此这是一份有效的XML文档。如果将4个子元素的顺序颠倒,则该文档就不再是一份有效的XML文档。
3.5.2 互斥的子元素
互斥的子元素表明一系列子元素之间只能出现其中之一。互斥子元素使用竖线(|)分隔,以竖线(|)分隔的多个子元素只能出现其中之一。多个互斥子元素以括号包括,表明多个子元素只能出现其中之一。对于如下的DTD定义:
程序清单:codes\03\3.5\choice.dtd
这份DTD定义与前面的DTD定义的区别在于:<书名…/>、<价格…/>、<作者…/>和<简要介绍…/>元素之间以竖线(|)分隔,这表明4个子元素只能出现其中之一,因此下面的XML文档是有效文档。
程序清单:codes\03\3.5\choice.xml
在上面的XML文档中,<计算机书籍…/>元素的子元素可以是<书名…/>、<价格…/>、<作者…/>或<简要介绍…/>子元素中的任意一个,只要不是同时出现两个或两个以上即可。因此,上面的XML文档是有效的XML文档。
3.5.3 子元素出现的频率
DTD不仅可以定义XML文档中的合法元素、各元素之间严格的父子关系,还可以定义各元素出现的次数:出现一次,出现多次,出现一次或多次等。
子元素的出现频率通过在元素声明后紧跟一个表示频率的特殊标记来表示,DTD中表示频率的特殊标记有如下3个:
+:表明子元素可以出现1次或多次。
*:表明子元素可以出现0次或多次。
?:表明子元素可以出现0次或1次。
如果在定义子元素时,没有在子元素后指定任何表示频率的特殊标记,则表明这些子元素只能出现一次,且必须出现一次。
注意
实际上,DTD对子元素的频率限制有4种,添加+、*和?频率限制符分别表示一种,不使用频率限制符也表示一种,它表明该子元素必须出现,而且只能出现一次。
看下面的DTD定义:
程序清单:codes\03\3.5\frequency.dtd
上面在定义<计算机书籍…>元素时,<书名…/>、<价格…/>、<作者…/>和<简要介绍…/>4个子元素只能出现其中之一。不过,<价格…/>子元素可以出现1次或多次,而<简要介绍…/>子元素则只可以出现0次或1次。
因此下面的XML文档也是有效的:
程序清单:codes\03\3.5\frequency.xml
上面的XML文档中,第1个<计算机书籍…/>元素包含1个<书名…/>子元素,第2个<计算机书籍…/>元素包含2个<价格…/>子元素,这完全符合DTD定义,DTD中定义了<价格…/>子元素可以出现1次或多次。第3个<计算机书籍…/>元素没有包括任何子元素,这也是符合DTD定义的,因为DTD定义了<计算机书籍…/>元素可以只包含<简要介绍…/>子元素,而该子元素后紧跟?,表明其可以出现0次或1次。
3.5.4 组合子元素
DTD允许使用圆括号将多个子元素括起来形成元素组,元素组内的各子元素之间可用英文逗号(,)分隔,以表示元素组之内各元素有序,也可用竖线(|)分隔,以表示元素组之内各元素互斥。
将指定元素编成一个元素组之后,DTD还允许使用表示频率的特殊标记紧跟该元素组,用以表示该元素组的出现频率。
例如有如下DTD:
程序清单:codes\03\3.5\group.dtd
在上面的DTD中将<书名…/>、<作者…/>两个元素编成了一个元素组,并使用+来修饰这个元素组,表明该元素组可以出现1次或多次。因此下面的XML文档是有效的:
程序清单:codes\03\3.5\group.xml
一旦使用圆括号将多个元素变成一个元素组,该元素组就成为一个整体,可被当成单个元素使用。看如下DTD定义:
程序清单:codes\03\3.5\group2.dtd
上面的DTD将<书名…/>元素和<作者…/>元素编成了一组(其中<书名…/>元素可出现1次或多次),又将<价格…/>元素和<简要介绍…/>元素编成了一组,最后用竖线(|)来分隔两个元素组,这表明两个元素组只能出现其中之一。因此下面的XML文档是有效的:
程序清单:codes\03\3.5\group2.xml
上述XML文档很好地遵守了上面的DTD中定义的语义约束,因此这份XML文档也是有效的。
3.5.5 无序的子元素
从理论上讲,DTD没有专门为定义无序子元素提供语法,如果希望使用DTD来表示某个元素之内可以接受无序的子元素,那该怎么办呢?这实际上也需要借助元素组来实现。
通过前面的介绍可以看出,用于表示频率的特殊标记不仅可以紧跟在元素之后,还可紧跟在以括号括起的元素组之后,表明该组元素的出现频率。看下面的DTD定义:
程序清单:codes\03\3.5\noSequence.dtd
上面的DTD定义表明,<书名…/>、<价格…/>、<作者…/>和<简要介绍…/>4个子元素形成了一个互斥组,在该元素组之内,这4个元素每次只能出现其中之一,然后使用加号(+)标识该元素组可以出现1次或多次。
假设有如下XML文档:
程序清单:codes\03\3.5\noSequence.xml
上面的XML文档看起来并不符合DTD所指定的规则。DTD定义了<书名…/>、<价格…/>、<作者…/>和<简要介绍…/>4个子元素只能出现其中之一,但文档中第1个<计算机书籍…/>元素包括了2个<作者…/>元素,而第2个<计算机书籍…/>元素里则包含了4个子元素,第3个<计算机书籍…/>元素也包含了4个子元素,而且4个子元素之间的顺序并不相同。
事实上,这4个元素形成了一个互斥组,该组元素每次只能出现4个之一,但出现哪个并没有确定。而这个元素组则可以出现1次或多次。如果该元素组恰好出现4次,每次出现的元素互不相同,则可能会出现第2个和第3个<计算机书籍…/>元素的情形。因此上面的XML文档是有效的。
学生提问:上面的DTD中明明定义了<书名…/>、<价格…/>、<作者…/>和<简要介绍…/>4个元素之间具有互斥关系,它们怎么可以同时出现呢?
答:<书名…/>、<价格…/>、<作者…/>和<简要介绍…/>4个元素确实具有互斥关系,不过需要指出的是:上面的DTD已经使用括号将它们编成了一组,也就是说它们仅仅在该元素组之内互斥,而并不是在<计算机书籍…/>元素之内互斥。假设一份XML文档的整体结构如图3.2所示。
图3.2 XML整体结构
从图中可以看出,该XML文档的根元素下包含3个<计算机书籍…/>子元素,其中第1个<计算机书籍…/>元素里包含2个元素组,第2个<计算机书籍…/>元素里包含4个元素组,第3个<计算机书籍…/>元素里也包含4个元素组。也就是说,<计算机书籍…/>元素的下一级对象是元素组,而不是单个元素。图3.2所示XML文档与DTD所定义的规则是符合的,如果其中所定义的元素组的格式是(书名|作者|价格|简要介绍),即4个元素中的任意一个,无论是哪个都是合法的,这样就可以形成如上所示的XML文档。
由此可见,虽然DTD本身没有提供定义无序子元素的方法,但实际使用中可以先将多个无序子元素定义成一个互斥的元素组,然后再使用+或*修饰元素组。