XML/多對多關係
學習目標
編輯完成本章內容的學習,您將能夠:
- 為包含有多對多關係的數據模型創建schema
- 在XML schema中使用XML包含(Include),並描述其作用
- 使用XML樣式表定義數值的顯示方式,同時我們也對樣式表中的其他功能進行了描述
- 使用更多特定的數據類型定義數值型元素.
- 理解在解析一個包含多對多關係的數據模型時應具備何種條件
概述
編輯- 在前面的章節中,我們介紹了XML schema和XML樣式表的特徵,以及如何為一個一對一關係建模。在本章中,我們將會進一
步介紹能夠用於描述元素標籤中數據的附加數據類型。使用XML樣式表,我們將會學會如何定義數據的顯示方式。另外,我們 將會介紹如何處理一個多對多關係,以及如何克服在XML應用這種關係時所遇到的問題。
多對多關係
編輯圖 5-1 m:m關係的數據模型
- 正如您所看到的,為了表示一個多對多關係,我們增添了兩個實體。該中間實體對於關聯實體數據模型的表示來說是必不可
少的,該關聯實體存儲了有關hotel和amenity關係的相關數據。在我們的Tour Guide例子中,我們添加了Amenity來表示 一個賓館所擁有的可以享用的便利設施列表。這個關係即為:一個賓館可以擁有多個便利設施、同一便利設施也可以被多家賓 館所擁有。在XML中表示多對多關係將會遇到很多問題。我們將在本章其後的內容中討論這些問題及相應的解決方案。
XML 模式
編輯- 隨着XML模式文件尺寸的逐步擴大;為了便於使用,我們很有必要將其分割成為若干獨立文檔。將模式分割成為多個文檔將
會使得對它的維護更加簡單,並能增強其的可讀性。這同時也意味着那些經常出現的元素可以在其他的模式中得到重用。這便是引入<include>元素的初衷。
- 為了校驗包含其它獨立模式的模式的有效性,include元素必須在其他任何元素出現之前進行聲明。
1 |
<?xml version="1.0" encoding="UTF-8" ?> |
圖 5-2 主XML模式– amenity.xsd
1 2 |
<?xml version="1.0" encoding="UTF-8" ?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema%22 elementFormDefault="unqualified"> |
圖 5-3 包含在主模式中的其它獨立XML模式– amenityList.xsd
1 2 |
<?xml version="1.0" encoding="UTF-8" ?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema%22 elementFormDefault="unqualified"> |
圖 5-4 定義continent的,包含在主模式中的獨立XML模式 – continents.xsd
- 注意元素continent現在是在一個獨立的模式(continents.xsd)中被創建的。使用這種方式,我們才可以在其他與本例
無關的XML模式中使用continent元素。AmenityList.xsd定義出了哪些字符串是便利設施。除了這些被包含的新的模式 外,還有一些值得我們注意的地方。我們添加了附加元素<lowerPrice> 和<upperPrice>,並把它們作為元素<hotel>的 屬性。這樣我們便可以跟蹤每一個<hotelDetails>的價格範圍。這兩個屬性都被定義為整型數值,同時<lowerPrice>的最 小值默認為1。新元素類型「positiveInteger」用來進一步規定能夠用於<population>, <lowerPrice>,和 <upperPrice>的整數的種類。
- 同時我們為<hotelDetails>添加了另外一個屬性。新的<amenity>元素使得我們能夠為每一家賓館記錄其所必須提供的便
利設施。一家賓館可以擁有多個便利設施,我們添加了maxOccurs=「unbounded」作為<sequence>的一個屬性。由於某些 賓館可能在<amenityValue>處並不提供任何數值信息,因此<amenityDetails>的minOccurs限制被設定為0。
多對多關係的表示中可能存在的問題
編輯- 在XML中,表示關係的最常用的方式是父子關係。父子關係可以直接用於表示一對一和一對多關係。但是XML並不直接支持多
對多關係。由於每一個元素可能擁有多於一個父元素,因此父子關係並不適用。下面是一些可能的解決方案:
建立那些不需使用多對多關係的XML文檔
- 通過限制XML文檔需要傳達的信息的範圍,我們可以避免使用多對多關係。不同於使用單一的一個XML文檔表示數據庫中的所
有信息,更好的方式是將信息進行分解,以使一個文檔僅僅描述多對多關係中的一個實體。在我們的tourGuide例子中,實 現上述設想的一種方式便是為每一家賓館創建一個獨立的XML文檔。這樣賓館與便利設施的關係最終可以轉變為一對多類型。 另外一種避免使用多對多關係的方式便是創建兩個模式。第一個模式為關係建立映射,而另外一個模式則用於檢驗關鍵數據的 取值進行有效性。
採用唯一的標識符來表示多對多關係
- 為每個實體創建唯一標識它的關鍵字,也是一種處理多對多關係的方法,儘管它不是最方便的方法。為了採用這種方法,在
XML schema中必須定義擁有ID或IDREF類型屬性的元素。ID表示父對象而IDREF則是子實體的主關鍵字。在tourGuide例子 中,擁有ID類型屬性的元素將成為賓館的獨有標識,而IDREF元素將用來規定每一個便利設施。但是,使用這種方式會在解析 多對多關係時產生其他的問題。
去除需要多對多關係的信息
- 解決這個問題的最簡單的方法就是去除需要多對多關係的信息。何時才用這種方法將主要取決於需要創建的文檔以及該文檔的目標讀者。
- 文檔設計者選擇多對多關係的表示方法時,並不是只選擇最有效率的信息表示方法。文檔的目標讀者以及該文檔的使用方式等因素都必須加以考慮。
XML Schema的附加數據類型
編輯- 在前面的章節中我們介紹了XML模式的預定義數據類型(例如年、月、時間、anyURI和日期)和自定義的數據類型定義方
法。我們已經介紹了一些基礎的數據類型的實例,例如整型和十進制小數類型。就像模式中的「positiveInteger」數據類 型,XML提供了許多數值型的數據類型,使用戶可以選擇使用它們來定義一個元素所允許取值的數字種類。下面的表格中列出 了可能的數值型數據類型:
名稱 | 描述 |
byte | 帶有正負號的8位整型 |
int | 帶有正負號的32位整型 |
long | 帶有正負號的64位整型 |
negativeInteger | 只有負值(..,-2,-1)的整型 |
nonNegativeInteger | 只有非負值(0,1,2,..)的整型 |
nonPositiveInteger | 只有非正值(.., -2, -1, 0)的整型 |
positiveInteger | 只有正值 (1, 2, ..)的整型 |
short | 帶有正負號的16位整型 |
unsignedLong | 不帶符號的64位整型 |
unsignedInt | 不帶符號的32位整型 |
unsignedShort | 不帶符號的16位整型 |
unsignedByte | 不帶符號的8位整型 |
有關數值類型數據的全部正式數據類型以及其他所有的數據類型,請參閱
XML文檔
編輯<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<HTML>
<HEAD>
<TITLE>World Reknowned Hotels</TITLE>
<STYLE TYPE="text/css">
H2 {TEXT-ALIGN:CENTER;}
.blueBackground {BACKGROUND-COLOR:LIGHTBLUE; TEXT-ALIGN:LEFT;
BORDER-WIDTH:0px;WIDTH:90%;}
.blackBackground {BACKGROUND-COLOR:BLACK; TEXT-ALIGN:LEFT;
COLOR:WHITE; FONT-SIZE:12pt; BORDER-WIDTH:2px;}
</STYLE>
</HEAD>
<BODY>
<H2>* * * HotelsListings by Country * * *</H2>
<xsl:apply-templates select="tourGuide"/>
</BODY>
</HTML>
</xsl:template>
<xsl:template match="tourGuide">
<xsl:for-each select="city">
<table CLASS="blackBackground">
<tr><td>
<table style="width:90%; color:white;" align="center"><tr><td>
<b><xsl:text>City: </xsl:text></b>
</td>
<td>
<xsl:value-of select="cityName"/>
</td></tr>
<tr><td><b><xsl:text>History: </xsl:text></b>
</td>
<td>
<xsl:value-of select="history"/>
</td></tr>
<xsl:for-each select="country">
<tr><td>
<b><xsl:text>Country: </xsl:text></b>
</td>
<td>
<xsl:value-of select="countryName"/>
</td></tr>
<tr><td>
<b><xsl:text>Population: </xsl:text></b>
</td>
<td>
<xsl:value-of select="format-number (population, '#,##0')"/>
</td></tr>
</xsl:for-each>
</TABLE> </td></tr>
<tr><TD colspan="2"><center>
<font style="font-size:medium">Hotels:</font>
<xsl:for-each select="hotel">
<table CLASS="blueBackground">
<tr>
<TD STYLE="FONT-SIZE:8pt;width:20%;">
<IMG>
<xsl:attribute name="SRC">
<xsl:value-of select="hotelPicture/@filename"/>
</xsl:attribute>
<xsl:attribute name="WIDTH">
<xsl:value-of select="hotelPicture/@size"/>
</xsl:attribute>
<xsl:attribute name="HEIGHT">
<xsl:value-of select="hotelPicture/@size"/>
</xsl:attribute>
<xsl:attribute name="ALT">
<xsl:value-of select="hotelPicture/@value"/>
</xsl:attribute>
</IMG>
</td>
<TD align="left" style="FONT-SIZE:10pt;">
<HR width="90%"/>
<b>
<xsl:text>Hotel Name: </xsl:text>
</b>
<xsl:value-of select="hotelName"/>
<b>
<xsl:text>Street Address: </xsl:text>
</b>
<xsl:value-of select="streetAddress"/>
<b>
<xsl:text>Telephone Number: </xsl:text>
</b>
<xsl:value-of select="telephoneNumber"/>
<b>
<xsl:text>Email Address: </xsl:text>
</b>
<xsl:value-of select="emailAddress"/>
<b>
<xsl:text>Price Range: $</xsl:text>
</b>
<xsl:value-of select="lowerPrice"/> - <xsl:value-of select="upperPrice"/>
<b>
<xsl:text>Hotel Rating: </xsl:text>
</b>
<xsl:value-of select="hotelRating"/>
<b>
<xsl:text>Amenities: </xsl:text>
</b>
<xsl:for-each select="amenities">
<xsl:value-of select="amenity"/>
<xsl:if test="position() &lt; last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
<HR width="90%"/>
</td>
</tr>
</TABLE>
</xsl:for-each>
</center>
</td></tr>
</TABLE>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
圖 5-4 包含新便利設施的XML文檔 – amenity.xml
XML樣式表
編輯- 與第三章的輸出格式類似,樣式表將XML文檔中的每家賓館按照所在國家進行排列。但是在本章中我們添加了一些新的屬
性。我們定義了價格範圍元素、列出了每家賓館的便利設施、而且對於每個國家的人口的顯示方式也進行了重新定義以提高數 據的可讀性。
XSLT函數
編輯- 在定義XML輸出格式時,有可能需要對於數據進行一些簡單的處理。在XML樣式表中,集成的函數將會在進行數據轉換時對於
數據進行處理。第三章中有一個顯示一個城市中賓館數量的例子:
- 為了增強人口信息的可讀性,我們需要對於人口數據按照通常的數字書寫格式為該數字分段(即3個數字一組,中間用逗號
隔開)。這樣我們就需要使用format-number( ) 函數。該函數的作用就是依照特定的模式將一個數值轉化成為字符串,該 特定的模式用來控制起始0的數目,以及千位之間的分割符。該函數的基本句法如下:
- number是待處理的數值
- pattern是定義了數值顯示格式的字符串。字符串中的每一個字符表示一位數字,或者其他的特殊的標點符號例如逗號或減號
- 為了將<population>按照三位一組、逗號分割的格式進行顯示,我們在XML樣式表中的第{51}行中使用了format-number函數。
- 下面列出了當在XML樣式表中使用format-number函數進行數據格式定義時,pattern中字符及其含義:
符號 |
含義 |
0 |
一位阿拉伯數字. |
- 另外在樣式表中還有大量其它的函數。下面列出了一些基於數值的函數,他們在進行數值處理時非常有用:
名稱 | 描述 |
ceiling() | 返回不小於指定數值的最小整數 |
floor() | 返回不大於指定數值的最大整數 |
number() | 將參數值轉化為數字 |
round() | 將參數數值四捨五入 |
sum() | 返回在一個節點集中所有數值數據的數值總和 |
1 2 |
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform%22> |
圖 5-5 XML樣式表– amenity.xsl
- 在第125行,您將看到兩個函數position()和last()。position函數返回當前內容節點在當前上下文節點集中的位置。
last函數返回在當前上下文中最後一個節點的索引值。為了列出每個賓館的每一項便利設施,轉換程序將檢查節點的當前位 置是否大於上下文節點集合中最後一個節點的位置。如果返回結果為真,便在該項便利設施後添加一個逗號。
- 儘管這裡的是依照第三章中的樣式表構建的,但是應該注意的是嵌入式表格的使用,它進一步規定了被傳送數據的格式。取
值及其標識被分別列在不同的單元格中,以便使得文檔具有更好的外觀和感覺。
解析過程中對於多對多關係的處理
編輯- 與您所選擇的多對多關係的不同表示方法相對應,在解析過程中會出現不同的問題。當解析一個多對多關係時,您首先需要
將其分解為兩個一對多關係以保證解析器可以正確處理。出於這個目的,我們必須對每個文檔分別進行解析。如果您要以獨立 的方式解析amenity.xml,您將發現每一個賓館的便利設施並沒有包含在已生成的插入語句中。因為我們必須首先解析定義 了每個賓館所有可能便利設施的模式(amenityList.xsd)。然後再解析tourGuide的主模式(amenity.xsd)
- 為了避免上述情況的發生,我們需要創建一個獨立的XML文檔來存儲每家賓館的便利設施列表,然後首先對其進行解析,最後再解析 tourGuideXML文檔。
總結
編輯- 當描述XML中的多對多關係是,我們必須首先將其分解為兩個一對多類型的關係。隨着XML模式文檔的逐步增大,模式設計者
會發現有必要將模式分割保存在彼此獨立的文檔中,並且通過包含聲明將其鏈接起來。這樣做的結果是,XML 模式擁有更高 的可讀性和易維護性。通過使用被定義的多種數值類型,我們可以對於數值型元素作更有針對性的描述。雖然在樣式表的轉換 過程中可以使用許多函數來進行數據處理,format-number()函數可以用於XML樣式表來定義在處理XML時數值類型取值的顯 示方式。
練習
編輯1)創建一個描述特殊類新餐館及其所在城市的XML模式。在該模式中應該包括每家餐館的價格範圍。當描述城市時,要包含一 個country元素。使用在 http://www.opentourism.org/xml/country.xsd 中列出的國家,創建一個獨立的模式,然後在您的主模式中使用<include>標籤對其進行引用。並進行良構性和有效性檢驗。
2)使用該schema創建一個描述了至少三家餐館而且至少有兩家餐館座落在同一城市中的XML文檔。並進行良構性和有效性檢驗。
3)創建一個按城市排列餐館的XML樣式表。在樣式表中需要記錄餐館的信息,正確定義價格範圍的顯示格式,並且使得表格中 的數據具有比較好的可讀性。