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文檔的良構性和有效 性。