跳转到内容

XML/递归关系

维基教科书,自由的教学读本
< XML

XML > 递归关系


学习目标 完成本章内容的学习,您将能够:

  • 理解递归关系的含义
  • 为一对一关系创建schema
  • 为一对多关系创建schema
  • 为多对多关系创建schema
  • 在schema中定义一个唯一标识
  • 创建主码/外码关系
  • 在XSL样式表中使用key()函数
  • 在XSL样式表中创建一个if-else结构
  • 在XSL中创建一个自定义实体

概述

[编辑]
递归关系概念较之前面介绍的几种关系(例如一对一关系、一对多关系、多对多关系)更为复杂,但也更加有趣。递归关系

在描述一个实体与其本身的关系时被使用。例如,当一个雇员是另外一些雇员的经理时便产生了一对多递归关系。实体“雇 员”与其本身发生了关系,在一个雇员(经理)与其它许多雇员(受该经理领导的人)之间存在一个一对多关系。由于现实中 存在着类似这样的复杂关系,我们需要采用一些稍稍复杂的方法将其映射到

一对一递归关系

[编辑]
让我们以美国的总统制度为例。除了第一任总统之外,每届总统都有他的前任。由于总统的前任也是总统,这样便产生了一

种递归关系。每一个前任只有一个继任的总统,而每一个总统也只有一个前任,所以这是一个一对一递归关系。虽然在数据建 模中不经常使用外码,但是使用外码可以使模型的较容易被理解。下图给出了如何为上述关系建模的示例。注意,前缀“PK”代表主码,“FK”代表外码。

Recursive

president.xml(一个包含一对一关系模型的XML文档)

[编辑]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="president.xsl"?>
<history xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="president.xsd">
<president primaryKey="1">
<firstName>George</firstName>
<lastName>Washington</lastName>
</president>
<president primaryKey="2">
<firstName>John</firstName>
<lastName>Adams</lastName>
<predecessor foreignKey="1"/>
</president>
<president primaryKey="3">
<firstName>Thomas</firstName>
<lastName>Jefferson</lastName>
<predecessor foreignKey="2"/>
</president>
</history>

表 6-1: President 实体的XML文档– president.xml

president.xsd (一个描述一对一关系模型的XML schema)

[编辑]
到现在为止,您还是没有看到如何使得一个元素以关系数据库的形式引用其他的元素。下面的schema便实现了上述操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:element name="history">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="president" type="presidentType"
maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType><
<xsd:keyref name="predecessorKey" refer="presKey">
<xsd:selector xpath="predecessor"/>
<xsd:field xpath="@foreignKey"/>
</xsd:keyref>
<xsd:unique name="presIdChecker">
<xsd:selector xpath="president"/>
<xsd:field xpath="@primaryKey"/>
</xsd:unique>
<xsd:unique name="oneToOneChecker">
<xsd:selector xpath="president/predecessor"/>
<xsd:field xpath="@foreignKey"/>
</xsd:unique>
<xsd:key name="presKey">
<xsd:selector xpath="president"/>
<xsd:field xpath="@primaryKey"/>
</xsd:key>
</xsd:element>
<xsd:complexType name="presidentType">
<xsd:sequence>
<xsd:element name="firstName" type="xsd:string"/>
<xsd:element name="lastName" type="xsd:string"/>
<xsd:element name="predecessor" type="predecessorType" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="primaryKey" type="xsd:long" use="required"/>
</xsd:complexType>
<xsd:complexType name="predecessorType">
<xsd:attribute name="foreignKey" type="xsd:long" use="required"/>
</xsd:complexType>
</xsd:schema>

表 6-2:实体的XML Schema– president.xsd

新元素介绍:

[编辑]
  • <xsd:unique>{15-22}。该元素用于定义一个唯一约束,也就是被约束的值不可以再被重复使用。这就相当于在数

据库中定义主码。na'me 属性是该唯一约束的专有标识。注意,当定义了多个唯一约束时,那么唯一约束的 na'me 属性取值一定要唯一。本例中的两个唯一约束起了非常关键的作用。第一个约束, “presIdChecker”{15-18}表明每一个总统必须有一个唯一的id。第二个约束,“oneToOneChecker”{19-22},用于 最终确定模型中所定义的一对一关系。在{32},一个总统被限制为仅有一位前任,但是我们仍需对于每个前任必然是唯一的 一个总统这个约束加以定义。请尝试将XML文件第{17}行中的前任值设置为“1”,您将发现更改后的文件将不再是有效的! 最后,只要对每个唯一性约束取一个唯一的名称,便可以定义多个唯一约束。在<xsd:unique>的元素中有两个是必不可少的:

    • <xsd:selector>在该元素中的xpath 属性记录了您所定义的唯一约束的父元素的路径。在上面的例子

中,我们已经确保了所有的president元素都是唯一的。注意,Xpath表达式仅仅局限于在该属性中使用,另外必须使用真正 的元素名称({20})而不能简单地使用“/”来表示其后的内容为继续搜索下一层子元素。

    • <xsd:field>,该元素中的xpath 属性记录了存储该唯一值的字段。在上面的例子中,便是名为

primaryKey的属性。注意,XML文件不同primaryKey的取值也不同(1,2,3)。如果出现了取值相同的情况,那么该XML 文件将不再有效。我们可以通过声明多个字段来创建数据库中复合主码在XML Schema中的映射。这里使用的Xpath表达式的 格式约束与元素<xsd:unique>中<xsd:selector>子元素的格式约束相同。

  • <xsd:key> {23-26}。该元素用于定义在父子关系中的父关键字,在本文的例子中用于一个概念上的主码/外码关

系。name属性是该约束的专有标识。在上面的例子中,它与在唯一约束中定义的信息相同。在元素 <xsd:key>中有两个必不可少的元素:

    • .<xsd:selector>该元素中的xpath 属性记录了到该父关键字的父元素的路径。Xpath表达式将遵循与元素<xsd:unique>中<xsd:selector> 子元素相同的格式约束。
    • <xsd:field>该元素中的xpath 属性记录了到存储有相应父关键字取值的字段的路径。Xpath表达式将

遵循与元素<xsd:unique>中<xsd:selector> 子元素相同的格式约束。

  • <xsd:keyref> {11-14}。该元素用于定义在父子关系中的子关键字,并且将其与相应的父元素连接起来。在我们

的例子中,它起到概念上的外码的作用。na'me 属性是该约束的专有标识。refer 属性尤为重 要,该属性的取值必须是<xsd:key>约束的名称(在本例中即为“pressKey”)。在元素 <xsd:keyref>中 有两个必不可少的元素:

    • .<xsd:selector>该元素中的xpath属性记录了到该子关键字的父元素的路径。Xpath表达式将遵循与元

素<xsd:unique>中<xsd:selector> 子元素相同的格式约束。

    • <xsd:field>该元素中的xpath 属性记录了到存储有相应子关键字取值的字段的路径。Xpath表达式将

遵循与元素<xsd:unique>中<xsd:selector> 子元素相同的格式约束。


 

欲知关于上述三种新元素的更多信息,请访问: http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/#element-unique

 

president.xsl(一个用于一对一关系模型的XSL样式表)

[编辑]
现在您已经了解如何创建一个定义概念上的主码/外码关系的schema,您现在需要进一步了解的是如何读取和显示这些数

据。请看下面示例中的样式表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:key name="predecessor" match="president" use="@primaryKey"/>
<!--xsl:key name="name referenced from within XSL file" match="element in XML file containing key"
use="attribute or sub-element containing actual value of key "-->
<!--key() function use: key('predecessor',2) will return the nodes contained
by the president element with attribute: primaryKey="2"
Since a node list is returned, <xsl:value-of select="key('predecessor',2)/firstName"/>
will output John -->
<xsl:output method="html"/>
<xsl:template match="/">
<table style="font-family:arial">
|----- style="background-color:#6495ED;font-weight:bold"
| President First Name || President Last Name
| Predecessor First Name || Predecessor Last Name
<xsl:for-each select="//president">
|----- style="background-color:#3CB371"
|
<xsl:value-of select="firstName"/>
|
<xsl:value-of select="lastName"/>


<xsl:choose>
<!--XSL equivolent to a programmatic if-else structure-->
<xsl:when test="predecessor">
<!--Test to see if there is a predecessor-->
|
<xsl:value-of select="key('predecessor',predecessor/@foreignKey)/firstName"/>
|
<xsl:value-of select="key('predecessor',predecessor/@foreignKey)/lastName"/>


</xsl:when>
<xsl:otherwise>
<!--Equivolent to else-->
| colspan="2" style="text-align:center" |
This is the first president!

</xsl:otherwise>
</xsl:choose>

</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>

表 6-3: President 实体的XML样式表 – president.xsl

新元素介绍:

[编辑]
  • <xsl:key> {4}在使用后面即将描述的key()函数前,必须先声明该元素。在该元素中有三个必不可少的属性:
    • name::该属性记录了在的key()函数中所引用XSL文档中对象的名称。name属性的取值可

以看作是编程语言中记录一个对象的实例的变量,比如java中的“String stringVariable”。

    • match:该属性与上面提到的.<xsd:selector>元素相似。它是在XML文件中记录

了父关键字(关系中的主码)的元素。

    • use:该属性与上面提到的<xsd:field>元素相似。是记录了父关键字取值的字段。
  • key(string,object)函数{33,36}:该函数根据提供的参数返回一组节点。string 参数是在<xsl:key>内定义的name属性的值。object 参数是您希望得到的节点的主码的取值,例如key('predecessor',2)将会返回包含在president元素中而且属性primaryKey= "2&quot的所有节点。当返回一组节点之后,您可以使用x-path表达式访问该组节点的任意子节点。因此<xsl:value-of select="key('predecessor',2)/firstName "/>将会返回结果“John”。关于key函数的更多信息请访问http://www.w3.org/TR/xslt#key 。
  • <xsl:choose>{28-45}该XSL元素提供了在XSL样式表中创建if-else类型结构的一种方法。这在我们的例子中是

必不可少的,因为第一届总统将不会拥有前任。我们仅仅在有前任的前提下显示某个总统的前任。为了实现这一点,在 lt;xsl:choose>中设置了两个属性:

    • <xsl:when>该属性相当于编程语言中的“if”语句。test属性包含了在第五章中提到的测试语句。在上

面的例子中,其用于检查是否存在predecessor元素(即被测试总统是否拥有前任)。

    • <xsl:otherwise>该属性相当于编程语言中的“else”语句。如果<xsl:when>的验证条件没有满足便显示其中的内容。 ^_^


 

一对多递归关系

[编辑]
我们将一个假想的运动队分成班,要求每班有一个班长。队中的每一个成员不管是不是班长,至少都是参赛者。由于一个班

的班长也是参赛者,这就产生了一个递归关系——一个班长既是参赛者,同时也与其他参赛者之间存在一个一对多的关系。这 个关系是一对多递归关系,因为一个班长下面将会有多个参赛者。下面是该关系的模型。同样需要注意的是:前缀“PK”代 表主码,“FK”代表外码。

Recursive

team.xml(一个包含一对多关系模型的XML文档)

[编辑]

注意xml中元素的名称与上面的模型不完全一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="team.xsl"?>
<sportsInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="team.xsd">
<team teamId="1">
<teamName>Southpark Lions</teamName>
<teamType>Football</teamType>
<player playerId="1">
<firstName>Eric</firstName>
<lastName>Cartman</lastName>
<height>4'6"</height>
<weight>170 lbs</weight>
</player>
<player playerId="2">
<firstName>Kyle</firstName>
<lastName>Broflovski</lastName>
<height>4'3"</height>
<weight>88 lbs</weight>
<squadCaptain fk_playerId="1"/>
</player>
<player playerId="3">
<firstName>Kenny</firstName>
<lastName>McCormmick</lastName>
<height>4'5"</height>
<weight>72 lbs</weight>
<squadCaptain fk_playerId="1"/>
</player>
<player playerId="4">
<firstName>Wendy</firstName>
<lastName>Testaburger</lastName>
<height>4'0"</height>
<weight>60lbs</weight>
</player>
<player playerId="5">
<firstName>Starvin' </firstName>
<lastName>Marvin</lastName>
<height>4'3"</height>
<weight>50 lbs</weight>
<squadCaptain fk_playerId="4"/>
</player>
<player playerId="6">
<firstName>Scott</firstName>
<lastName>Tenorman</lastName>
<height>5'1"</height>
<weight>130 lbs</weight>
<squadCaptain fk_playerId="4"/>
</player>
</team>
<team teamId="2">
<teamName>Athens Hoops</teamName>
<teamType>Basketballl</teamType>
<player playerId="7">
<firstName>Bill</firstName>
<lastName>Slammin</lastName>
<height>6'9"</height>
<weight>205lbs</weight>
</player>
<player playerId="8">
<firstName>Johnny</firstName>
<lastName>Rebound</lastName>
<height>6'6"</height>
<weight>199 lbs</weight>
<squadCaptain fk_playerId="7"/>
</player>
<player playerId="9">
<firstName>Ted</firstName>
<lastName>Dribble</lastName>
<height>7'0"</height>
<weight>230lbs</weight>
<squadCaptain fk_playerId="7"/>
</player>
<player playerId="10">
<firstName>James</firstName>
<lastName>Shooter</lastName>
<height>7'1"</height>
<weight>205lbs</weight>
</player>
<player playerId="11">
<firstName>Michael</firstName>
<lastName>Keller</lastName>
<height>6'6"</height>
<weight>210 lbs</weight>
<squadCaptain fk_playerId="10"/>
</player>
<player playerId="12">
<firstName>Jerry</firstName>
<lastName>Pryor</lastName>
<height>6'3"</height>
<weight>180lbs</weight>
<squadCaptain fk_playerId="10"/>
</player>
</team>
</sportsInfo>

表 6-4: Team 实体的XML 文档 – team.xml  

team.xsd(一个描述一对多关系模型的XML schema)

[编辑]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:element name="sportsInfo">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="team" type="teamInfo" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:key name="playerKey">
<xsd:selector xpath="player"/>
<xsd:field xpath="@playerId"/>
</xsd:key>
<xsd:unique name="teamChecker">
<xsd:selector xpath="team"/>
<xsd:field xpath="@teamId"/>
</xsd:unique>
<xsd:unique name="playerChecker">
<xsd:selector xpath="team/player"/>
<xsd:field xpath="@playerId"/>
</xsd:unique>
<xsd:keyref name="captainForeignKey" refer="playerKey">
<xsd:selector xpath="squadCaptain"/>
<xsd:field xpath="@fk_playerId"/>
</xsd:keyref>
</xsd:element>
<xsd:complexType name="teamInfo">
<xsd:sequence>
<xsd:element name="teamName" type="xsd:string"/>
<xsd:element name="teamType" type="xsd:string"/>
<xsd:element name="player" type="playerInfo" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="teamId" type="xsd:long" use="required"/>
</xsd:complexType>
<xsd:complexType name="playerInfo">
<xsd:sequence>
<xsd:element name="firstName" type="xsd:string"/>
<xsd:element name="lastName" type="xsd:string"/>
<xsd:element name="height" type="xsd:string"/>
<xsd:element name="weight" type="xsd:string"/>
<xsd:element name="squadCaptain" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="fk_playerId" type="xsd:long"/>
<xsd:attribute name="playerId" type="xsd:long" use="required"/>
</xsd:complexType>
</xsd:schema>

表 6-5: Team实体的XML schema – team.xsd

{41}规定了一个参赛者只能拥有一个班长,这样便为该关系创建了一对多约束。<xsd:unique>元素的嵌套使用改

变了该约束的范围。注意在{18-21}中<xsd:unique>的位置——在sportInfo主元素的定义之中。这对于确保在同 一XML文档中没有任意两个参赛者拥有相同的ID是十分必要的。如果元素 <xsd:unique>仅被置于team元素的定义之 中,那么它就只能约束在一个队中,参赛者的ID唯一——也就意味着属于不同队的参赛者可能拥有相同的ID。  

==team.xsl(一个用于一对多一关系模型的XSL样式表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stylesheet [<!ENTITY space "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>">]>
<!-- The namespace attribute above is only necessary for XML parsers using the MSXML parser-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:key name="squadCaptain" match="player" use="@playerId"/>
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select="//team">
<table>
|----- style="background-color:#6495ED;font-weight:bold"
| colspan="3" |
Team Id: <xsl:value-of select="@teamId"/><br />
Team Name: <xsl:value-of select="teamName"/><br />
Team Type: <xsl:value-of select="teamType"/>
|----- style="background-color:#F0E68C;font-weight:bold"
| Player Id || Player Name || Player Captain
<xsl:for-each select="player">
|----- style="background-color:#D3D3D3"
| <xsl:value-of select="@playerId"/>
| <xsl:value-of select="firstName"/>&space;
<xsl:value-of select="lastName"/>

<xsl:choose>
<xsl:when test="squadCaptain">
|
<xsl:value-of select="key ('squadCaptain',squadCaptain/@fk_playerId)/firstName"/>
&space;<xsl:value-of select="key ('squadCaptain',squadCaptain/@fk_playerId)/lastName"/>						
</xsl:when>
<xsl:otherwise>
| style="background-color:#FF0000;font-weight:bold" | Squad Captain
</xsl:otherwise>
</xsl:choose>

</xsl:for-each>	
</table>
<br />
</xsl:for-each>	
</xsl:template>
</xsl:stylesheet>

表6-6: Team实体的XML样式表 – team.xsl

新元素介绍

[编辑]
  • <!doctype stylesheet[….]> {2} – 该声明在声明一个实体时是必不可少的,而且必须出现在<xsl:stylesheet>标签之前。
  • <!ENTITY space "<xsl:text xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> </xsl:text>"> {2}- 该声明必须在上述文档类型声明中的中括号“[]”之间进行。“space”就是被定

义的实体名称。双引号之间的值就是该实体所代表的事物。在我们的例子中,实体space 代表了一个空格,可通过 <xsl:text> </xsl:text>语句表示该实体。元素<xsl:text>中的xmlns 属性仅在使用 Microsoft XML解析器解析文档时起作用,但是为了实现更好的兼容性,应该在样式表中进行定义。使用该实体的句法与在 XSL样式表中使用其他实体时所使用的句法相同。即在实体名称前面加(“&”),后面再加一个分号。在上面的例子中, 即为{30}中的 &space;。这样无论在样式表的何处需要一个空格,便可以用““&space;”代替<xsl:text> </xsl:text>。

 

多对多递归关系

[编辑]
到目前为止您是否已经对于递归关系有一个初步的认识?在这里我们将继续介绍第三种递归关系——多对多递归关系。多对

多关系发生的通常情况是一个实体由与其类型相同的许多实体构成,并且每个子实体属于同类型的其他父实体。听起来比较糊 涂,是么?我们来设想一辆在专卖店销售的汽车。这辆汽车拥有许多可选的零部件,例如电动车窗、真皮座椅等。顾客可以将 这些零部件看作一个零部件集合全部购买下来,例如奢侈品包。这个集合由许多单独出售的零部件所组成,而且每一个部件都 有可能属于其他零件集合。注意,虽然任何一个特定的零部件可能只能用于特定的汽车,但是为了简单起见,该约束并没有加 入到下文的模型中。  

Recursive
注意:这里的“Package”与其本身之间有一个多对多关系。所以我们需要添加一个中间实体“Accessory”来将此多对多关系简化成为两个一对多关系。将一个复杂的多对多关系转化成为两个一对多关系通常是一种很好的处理方法。

accessory.xml(一个包含多对多关系模型的XML文档)

[编辑]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="accessory.xsl"?>
<carInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="accessory.xsd">
<accessory accessoryId="1">
<accessoryName>Power Windows</accessoryName>
<accessoryPrice>500.00</accessoryPrice>
</accessory>
<accessory accessoryId="2">
<accessoryName>Power Locks</accessoryName>
<accessoryPrice>350.99</accessoryPrice>
</accessory>
<accessory accessoryId="3">
<accessoryName>Power Seats</accessoryName>
<accessoryPrice>400.00</accessoryPrice>
</accessory>
<accessory accessoryId="4">
<accessoryName>Leather Seats</accessoryName>
<accessoryPrice>700.00</accessoryPrice>
</accessory>
<accessory accessoryId="5">
<accessoryName>Sunroof</accessoryName>
<accessoryPrice>200.00</accessoryPrice>
</accessory>
<accessory accessoryId="6">
<accessoryName>Bose Stereo</accessoryName>
<accessoryPrice>700.00</accessoryPrice>
</accessory>
<accessory accessoryId="7">
<accessoryName>Power Package</accessoryName>
<accessoryPrice>1000.00</accessoryPrice>
<subProduct fk_SubProductId="1"/>
<subProduct fk_SubProductId="2"/>
<subProduct fk_SubProductId="3"/>
</accessory>
<accessory accessoryId="8">
<accessoryName>Luxury Package</accessoryName>
<accessoryPrice>2500.00</accessoryPrice>
<subProduct fk_SubProductId="1"/>
<subProduct fk_SubProductId="2"/>
<subProduct fk_SubProductId="3"/>
<subProduct fk_SubProductId="4"/>
<subProduct fk_SubProductId="5"/>
<subProduct fk_SubProductId="6"/>
</accessory>
</carInfo>

表 6-9:Accessory实体的XML文档 – accessory.xml

 

accessory.xsd(一个描述多对多关系模型的XML schema)

[编辑]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:element name="carInfo">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="accessory" type="accessoryInfo" minOccurs="0"
maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:keyref name="subProductKey" refer="accessoryKey">
<xsd:selector xpath="subProduct"/>
<xsd:field xpath="@fk_SubProductId"/>
</xsd:keyref>
<xsd:unique name="accessoryChecker">
<xsd:selector xpath="accessory"/>
<xsd:field xpath="@accessoryId"/>
</xsd:unique>
<xsd:key name="accessoryKey">
<xsd:selector xpath="accessory"/>
<xsd:field xpath="@accessoryId"/>
</xsd:key>
</xsd:element>
<xsd:complexType name="accessoryInfo">
<xsd:sequence>
<xsd:element name="accessoryName"/>
<xsd:element name="accessoryPrice" type="xsd:decimal"/>
<xsd:element name="subProduct" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="fk_SubProductId" type="xsd:long"
use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="accessoryId" type="xsd:long" use="required"/>
</xsd:complexType>
</xsd:schema>

表 6-8: Accessory实体的XML schema – accessory.xsd

到这里您可能发现一对一、一对多、多对对关系的schema会有一些细微的不同。请看上面schema中的第28行语句,正是它

实现了多对多关系的定义。这个概念schema与一对多关系的schema唯一的不同之处在于第28行语句,maxOccurs 属性被赋值为unbounded而不是1。这表明一个零件可以由多个子产品组成。由于没有其他的约束,一个零件也可以属于其他 多个产品。  

==accessory.xsl(一个用于多对多一关系模型的XSL样式表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:key name="subProduct" match="accessory" use="@accessoryId"/>
<xsl:output method="html"/>
<xsl:template match="/">
<table>
|----- style="background-color:#6495ED;font-weight:bold"
| Accessory/Package || Accessory/Package Price
| Subproduct(s)


<xsl:for-each select="//accessory">
|----- style="background-color:#D3D3D3;vertical-align:top;"
|
<xsl:value-of select="accessoryName"/>
|
<xsl:value-of select="format-number(accessoryPrice,'$###,###.00')"/>


<xsl:choose>
<xsl:when test="subProduct ">
|
<table>
|----- style="background-color:#F0E68C;font-weight:bold"
| Accessory Name || Accessory Price
<xsl:for-each select="subProduct">
|----- style="background-color:#7B68EE"
| <xsl:value-of select="key('subProduct',@fk_SubProductId)/accessoryName"/>
| <xsl:value-of select="format-number(key ('subProduct',@fk_SubProductId)/accessoryPrice,'$###,###.00')"/>


</xsl:for-each>
</table>							
</xsl:when>
<xsl:otherwise>
| N/A

</xsl:otherwise>
</xsl:choose>

</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>

表 6-9: Accessory实体的XML样式表 – accessory.xsl

总结

[编辑]
  • 在父子类型数据关系中,当子元素与其父元素有着相同的数据类型时,表明存在一个递归关系。
  • 元素<xsd:unique>在schema中可以用于创建对于XML文档中取值唯一的约束。
  • 元素<xsd:key> 和 <xsd:keyref>在schema中可以用于定义XML文档中的一个父子关系。
  • key()函数在XSL样式表中可以用于在父子关系中根据子元素的取值而得到父元素的值。
<xsl:choose> 
  <xsl:when test= “..”> 
  </xsl:when>
  <xsl:otherwise> 
  </xsl:otherwise> 
</xsl:choose> 在XSL中创建一个if-else结构
  • 您可以在XSL样式表中声明一个实体,从而为特定功能的调用创建快捷方式。

练习

[编辑]
  • 创建一个描述接力赛参赛队伍的schema。其中需要包含参赛队员以及接力赛中各队员的接棒次序。创建一个XML文件,其

中应该包含该schema的示例数据并且至少有4名队员的数据。验证该XML文档的良构性和有效性。

  • 每个国家都会有很多保险销售人员,并且在该国家的每一个地区(东、西、南、北)都会有地区性的销售总管。每一个销

售人员只能在一个地区从事销售工作并且只能从属于一个销售总管。创建一个描述上述关系的schema。创建一个拥有两个国 家数据的XML文档。确保该XML文档的良构性和有效性。每个国家必须包含至少两个地区,每个地区至少拥有3个销售人员,其 中包括销售总管。每一个销售人员应该有唯一的数字进行标识。每个国家由两个字母的缩写进行唯一标识。创建一个XSL样式 表来显示这些数据,要求每个国家显示在单独的表格中。如果一个销售人员同时也是地区总管,应该在显示中有所表示。在样 式表中创建一个名为doublespace的实体,其等同于连续两个回车。使用这个新创建的实体在两个表格之间生成两个换行。

  • 一个餐馆在其菜单上可能会有多道开胃菜。而且餐馆也会做出包含有一小部分选定开胃菜品的拼盘样品。创建一个描述上

述关系的schema。创建一个记录了足够多品种的开胃菜以及至少两种拼盘样品的XML文档。验证该XML文档的良构性和有效 性。