DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 使用 XPath 2.0 和 XSLT 2.0 節省開發時間並減少代碼量
使用 XPath 2.0 和 XSLT 2.0 節省開發時間並減少代碼量
編輯:XML詳解     

 本文示例源代碼或素材下載

  XPath 2.0 和 XSLT 2.0 中主要的新概念之一是,一切都是序列。在 XPath 1.0 和 XSLT 1.0 中,您通常要使用節點樹。解析後的 XML 文檔是一個節點樹,包含了文檔節點及其後代節點。您可以使用該節點樹查找根元素及其所有的後代元素、屬性和兄弟元素的節點。(XML 文件中根元素之外的所有注釋或處理說明都被看作根元素的兄弟元素。)

  當您在 XPath 2.0 和 XSLT 2.0 中使用 XML 文檔時,序列的使用方式與 XPath 1.0 和 XSLT 1.0 中樹結構的使用方式相同。序列包含了一個 item(文檔節點),它的使用方式與以前相同。但是,您可以創建原子值 序列。您將在後面的一個示例應用程序中管理一組錦標賽數據,這是一場由 16 支隊伍參加的單場淘汰賽,取自該程序的 清單 1 展示了一個原子值序列的示例。

  清單 1. 一個原子值序列

<xsl:variable name="seeds" as="xs:integer*">
 <xsl:sequence
  select="(1, 16, 8, 9, 5, 12, 4, 13, 6, 11, 3, 14, 7, 10, 2, 15)"/>
</xsl:variable>

  此代碼定義了變量 $seeds。如您所料,新的 <xsl:sequence> 元素定義了 item 序列。此時,item 是 XML Schema xs:integer。新的 as 屬性定義了變量的數據類型,而星號(xs:integer*)指序列包含零或更多整數。在 XPath 1.0 和 XSLT 1.0 中,您將創建 16 個不同的文本節點,然後將這些節點聚合到一個變量中。在 XPath 2.0 和 XSLT 2.0 中,序列作為一個一維數值數組使用,這正是示例應用程序中所需要的用法。

  序列遵循了一些規則。首先,序列不能包含其他序列。如果您使用一個由三個 item 組成的序列,和其後的另外一個由三個 item 組成的序列,一起創建新序列,則得到的結果是一個包含六個 item 的新序列。其次,序列允許您混合使用節點和 item。您可以創建一個序列,其中包含 清單 1 所示的原子值,以及 清單 2 所示的所有 <contestant> 元素。

  清單 2. 節點和原子值序列

<xsl:variable name="seeds" as="item()*">
 <xsl:for-each select="/bracket/contestants/contestant">
  <xsl:copy-of select="."/>
 </xsl:for-each>
 <xsl:sequence
  select="(1, 16, 8, 9, 5, 12, 4, 13, 6, 11, 3, 14, 7, 10, 2, 15)"/>
</xsl:variable>

  $seeds 變量包含了所有的競爭節點和您先前使用的 16 個原子值。注意,變量的數據類型是 item()*。每個 item 是一個節點或原子值,因此此變量可以包含任何內容。

  現在,您已經了解了序列和 item 的基本信息,我們開始考察 to 運算符,它也是 XPath 2.0 和 XSLT 2.0 中的新特性。它讓您能夠選擇一些整數。例如,您可以創建如 清單 3 所示的序列。

  清單 3. 使用 to 運算符創建的整數序列

<xsl:variable name="range" as="item()*">
 <xsl:sequence select="1 to 16"/>
</xsl:variable>

  此代碼創建的變量 $range 包含了從 1 到 16 的整數。在同一個應用程序中,您可以使用 to 運算符作為循環機制,如 清單 4 所示。

  清單 4. 使用 to 運算符實現循環

<xsl:for-each select="1 to 32">
 <!-- Do something useful here -->
</xsl:for-each>

  在您構建樣式表之前,請仔細查看示例應用程序。

  理解示例應用程序

  在本文的示例應用程序中,您將為 16 支隊伍參加的單場淘汰錦標賽管理賽事數據。如您所料,錦標賽數據以 XML 格式表示。您將創建一個 XSLT 2.0 樣式表,將 XML 數據轉換為 Html 表以顯示錦標賽的結果。清單 5 展示了 XML 文檔的格式。

  清單 5. 含有錦標賽數據的 XML 文檔

<?XML version="1.0" encoding="UTF-8"?>
<!-- tourney.XML -->
<bracket>
  <title>RSDC SMackdown</title>
  <contestants>
   <contestant seed="1" image="images/homerSimpson.png">Donuts</contestant>
   <contestant seed="2" image="images/caffeine.png">Caffeine</contestant>
   <contestant seed="3" image="images/fearlessFreep.png">Fearless Freep</contestant>
   <contestant seed="4" image="images/wmd.jpg">Weapons of Mass Destruction</contestant>
   <contestant seed="5" image="images/haroldPie.jpg">PIE</contestant>
   <contestant seed="6" image="images/adamAnt.png">Adam Ant</contestant>
   <contestant seed="7" image="images/georgeWBush.jpg">Misunderestimated</contestant>
   <contestant seed="8" image="images/sillyPutty.jpg">Silly Putty</contestant>
   <contestant seed="9" image="images/krazyGlue.jpg">Krazy Glue</contestant>
   <contestant seed="10" image="images/snoopDogg.png">Biz-Implification</contestant>
   <contestant seed="11" image="images/atomAnt.png">Atom Ant</contestant>
   <contestant seed="12" image="images/ajaxcan.png">AJax</contestant>
   <contestant seed="13" image="images/darthVader.jpg">Darth Vader</contestant>
   <contestant seed="14" image="images/nastyCanasta.png">Nasty Canasta</contestant>
   <contestant seed="15" image="images/jcp.png">Java Community Process</contestant>
   <contestant seed="16" image="images/andre.png">Andre the Giant</contestant>
  </contestants>
  <results>
   <result round="1" firstSeed="1" secondSeed="16" winnerSeed="1"/>
   <result round="1" firstSeed="8" secondSeed="9" winnerSeed="9"/>
   <result round="1" firstSeed="5" secondSeed="12" winnerSeed="5"/>
   <result round="1" firstSeed="4" secondSeed="13" winnerSeed="4"/>
   <result round="1" firstSeed="6" secondSeed="11" winnerSeed="11"/>
   <result round="1" firstSeed="3" secondSeed="14" winnerSeed="3"/>
   <result round="1" firstSeed="7" secondSeed="10" winnerSeed="10"/>
   <result round="1" firstSeed="2" secondSeed="15" winnerSeed="2"/>
   <result round="2" firstSeed="1" secondSeed="9" winnerSeed="1"/>
   <result round="2" firstSeed="5" secondSeed="4" winnerSeed="5"/>
   <result round="2" firstSeed="11" secondSeed="3" winnerSeed="3"/>
   <result round="2" firstSeed="10" secondSeed="2" winnerSeed="2"/>
   <result round="3" firstSeed="1" secondSeed="5" winnerSeed="1"/>
   <result round="3" firstSeed="3" secondSeed="2" winnerSeed="2"/>
   <result round="4" firstSeed="1" secondSeed="2" winnerSeed="2"/>
  </results>
</bracket>   

  在 <title> 元素中,您可以看見錦標賽的名稱 RSDC SMackdown。此文檔表示了今年 IBM Rational Software Developer Conference 中某個會議的真實結果。

  一個方括號包含了 16 個 <contestant> 元素,每個元素擁有一個名稱(它的文本)、一個 seed 和一個圖像。16 支隊伍的錦標賽包含了 15 場比賽。每場比賽由一個 <result> 元素表示。每場比賽具有四組關聯數據:發生比賽的錦標賽回合(round 屬性),兩個競爭隊伍的 seed(存儲於 firstSeed 和 secondSeed 屬性中),以及獲勝者的 seed(winnerSeed 屬性)。您的任務是獲取此 XML 文檔並將其轉換為顯示結果的 Html 表格,如 圖 1 所示。

  圖 1. Html 表中的錦標賽結果

使用 XPath 2.0 和 XSLT 2.0 節省開發時間並減少代碼量

  該表為 32 行 5 列。您可以使用 XSLT 1.0 的方法構建 Html 表,每次一行,如 清單 6 所示。

  清單 6. 構建 Html 表,每次一行

<!-- Row 1 -->
<tr>
 <td style="border: none; border-top: solid; border-right: solid;">
  <xsl:text>[1] </xsl:text>
  <xsl:value-of select="$contestants[@seed='1']/>
 </td>
 <td style="border: none;>
  &nbsp;
 </td>
 <td style="border: none;>
  &nbsp;
 </td>
 <td style="border: none;>
  &nbsp;
 </td>
 <td style="border: none;>
  &nbsp;
 </td>
</tr>
<!-- Row 2 -->
. . .

  這種方法可行,但是樣式表會難於維護。整個樣式表中每行和每列都有重復的代碼。這裡的主要問題是,輸出表中需要有 32 行。每一行包含來自 XML 文檔中的元素數據(<contestant> 或 <result>)。不幸的是,您並沒有 32 個元素可以迭代。您可以使用 <xsl:for-each select="contestants/contestant|results/result">,但是這些元素在表中的顯示順序並不符合要求。XSLT 1.0 沒有提供什麼工具幫助您解決這一問題。

  您可以重構代碼以簡化樣式表。在樣式和單元格內容方面有一些模式,您可以使用 XPath 2.0 和 XSLT 2.0 的新特性在這些模式中迭代。最終的樣式表比(公認笨拙的)原始版本小了大約 70%。在構建該樣式表之前,我們來查看一下如何重構代碼。

  重構代碼 —— 計算表單元格的樣式

  要重構代碼,首先將樣式信息移到 CSS 樣式表中。如 圖 2 所示,Html bracket 表有 5 個不同的單元格樣式:None、MatchupStart、MatchupMiddle、MatchupEnd 和 Solid。

  圖 2. Html 表中的單元格邊界樣式

使用 XPath 2.0 和 XSLT 2.0 節省開發時間並減少代碼量

  清單 7 顯示了 CSS 代碼的外觀。

  清單 7. CSS 樣式表

.None     { width: 20%; }
.MatchupStart { width: 20%;
         border: none; border-top: solid;
         border-right: solid; }
.MatchupMiddle { width: 20%;
         border: none; border-right: solid; }
.MatchupEnd  { width: 20%;
         border: none; border-bottom: solid;
         border-right: solid; }
.Solid     { width: 20%;
         border: solid; }

  邊界樣式的使用使查看錦標賽的結果非常容易。連接兩個單元格的水平線表示這兩個競爭者將會彼此遭遇;下面的列中使用實線邊界的單元格指比賽的獲勝者。在邊界樣式表中,您可以看到一個明確的模式:對於每一對競爭者,第一個競爭者前的單元格沒有邊界(樣式 None),兩個競爭者的單元格擁有一個實線邊界(樣式 Solid),兩個競爭者之間的單元格在右側有一個邊界(樣式 MatchupMiddle),而最後一個競爭者後的單元格沒有邊界(樣式 None)。對第一列有少許不同。因為第一列中的競爭者對太過接近,所以第一個競爭者的單元格有上邊界和右邊界(樣式 MatchupStart),而第二個競爭者的單元格有下邊界和右邊界(樣式 MatchupEnd)。

  注意,每一列中競爭者對相距很遠。在第一列中競爭者之間相距一個單元格的距離,在第二列中相距三個單元格的距離,在第三列中相距七個單元格的距離,在第四列中相距 15 個單元格的距離。每個距離的大小為 2 的冪次方減一,因此這裡有一個明確的模式。

  表的一個通用模式是每一列包含一組重復的單元格。這五列的重復組(我稱之為列的 period,因為沒有更好的術語)的大小為 4、8、16、32 和 64。每個重復組包含兩個競爭者及其之間、之前和之後的單元格。

  在每一列中,使用計算中用到的兩個值:$period,表示重復組的大小;以及 $oneQuarter,表示 period 大小的四分之一(將 $period div 4 存儲在一個變量中使代碼更加整潔。)表 1 展示了單元格邊界樣式的通用規則。

  表 1. Html 表中的單元格邊界樣式

公式 第一列(period 4) 第二列(period 8) 第三列(period 16) 第四列(period 32) 第五列(period 64) $row < $oneQuarter 或者 $row > $period - $oneQuarter N/A 第 1 行,樣式 None 第 1-3 行,樣式 None 第 1-7 行,樣式 None 第 1-15 行,樣式 None $row = $oneQuarter 第 1 行,樣式 MatchupStart 第 2 行,樣式 Solid 第 4 行,樣式 Solid 第 8 行,樣式 Solid 第 16 行,樣式 Solid $row > $oneQuarter 和 $row < $period - $oneQuarter 第 2 行,樣式 MatchupMiddle 第 3-5 行,樣式 MatchupMiddle 第 5-11 行,樣式 MatchupMiddle 第 9-23 行,樣式 MatchupMiddle 第 17-32 行,樣式 None $row = $period - $oneQuarter 第 3 行,樣式 MatchupEnd 第 6 行,樣式 Solid 第 12 行,樣式 Solid 第 24 行,樣式 Solid N/A $row < $oneQuarter 或 $row > $period - $oneQuarter 第 4 行,樣式 None 第 7-8 行,樣式 None 第 13-16 行,樣式 None 第 25-32 行,樣式 None N/A

  現在,您已經創建了一種優雅的方式來指出表中任何一個給定單元格的樣式。指定列號和行號後,這些公式將發揮它們的作用。

  重構代碼 —— 計算表單元格的內容

  當然,您在表中創建單元格時也需要往單元格中放入適當的內容。這也有一個很好的模式。樣式 None 或 MatchupMiddle 的部分沒有任何內容。就是說您只需為其他三種樣式尋找適當的內容。

  在 表 1 中,您可以看見,帶內容的單元格擁有 $row = $oneQuarter 或 $row = $period - $oneQuarter 屬性。對於第一列,您只需寫入 seed 和相應競爭者的名稱。比賽基於 seed 排名並形成了 表 2 所示的對。

  表 2. 比賽基於 seed 排名

[1] 對陣 [16] [8] 對陣 [9] [5] 對陣 [12] [4] 對陣 [13] [6] 對陣 [11] [3] 對陣 [14] [7] 對陣 [10] [2] 對陣 [15]

  比賽安排的結果是如果 seed 排名較高的隊伍一直贏,那麼 seed 排名最高的隊伍將一直對陣 seed 排名最低的隊伍,seed 排名次高的隊伍將一直對陣 seed 排名次低的隊伍,依此類推。查看 seed 的順序(1、16、8、9、5、12、4、13、6、11、3、14、7、10、2、15),您可以看見它們與 $seeds 序列中的值相匹配。競爭者在第一列中按此順序顯示。

  每隔一行顯示一個競爭者,即,第 1 行擁有序列中的第一個 seed,第 3 行擁有序列中的第二個 seed,第 5 行擁有序列中的第三個 seed,等等依此類推。這裡的模式是 $row + 1 div 2 = $index。換言之,獲取行號,加 1,然後除以 2 得到 $seeds 序列中 seed 的索引。表單元格的內容是 seed 編號,後接具有該 seed 級別的競爭者的名稱。

  第一列的內容現已處理好。如您所料,第 2 到 5 列就要復雜一些。您不需要查看 <contestant> 元素,而需要查看結果,顯示於 15 個 <result> 元素中。

  第 2 列包含了第 1 回合的獲勝者。就是指您需要查看具有 round="1" 屬性的 <result> 元素。為了簡化起見,第一場比賽(seed 1 對陣 seed 16)的獲勝者存儲於第一個 <result> 元素中,第二場比賽(seed 8 對陣 seed 9)的獲勝者存儲於第二個 <result> 元素中,等等依此類推。第二列的獲勝者在第 2、6、10、14、18、22、26 和 30 行中顯示。觀察其模式,第 2 行使用第一個 <result> 元素,第 6 行使用第二個該元素,第 10 行使用第三個該元素。對於此列,如果您取得行號,加 2,然後除以 4,您將獲得正確的 <result round="1"> 元素的位置。

  第 3 和 4 列按類似的方法處理。第 3 列的獲勝者顯示在第 4、12、20 和 28 行(此處的模式為加 4 然後除以 8)。第 4 列中的獲勝者顯示於第 8 和 24 行中(加 8 並除以 16)。第 5 列只顯示了一個競爭者 — 整個錦標賽的獲勝者。如果您位於第 16 行,您將在單個的 <result round="4"> 元素中顯示獲勝者。

  重構以找出您所搜尋的值的位置的方法是使用 ($row + $oneQuarter) div ($oneQuarter * 2)。使用重復模式的遞增的大小使代碼簡化。對於第 1 列,計算後的位置是 seed 序列的索引;對於其他列,計算後的位置是正確的 <result> 元素的位置。

  現在,您已經掌握了一種優雅的方式,確定表中每個單元格的內容和樣式。指定行號和列號後,您可以計算出所有需要了解的內容。

  利用 XPath 2.0 和 XSLT 2.0 的功能

  現在,您可以使用 XPath 2.0 和 XSLT 2.0 中的新技術簡化樣式表設計。開始時使用 to 運算符。如果您使用過程式編程語言構建表,那麼可能要編寫類似 清單 8 的代碼。

  清單 8. 過程式方法處理樣式表

   for (int row=1; row<=32; row++)
    for (int column=1; column<=5; column++)
     // Build each cell in the table here

  在 XPath 2.0 和 XSLT 2.0 中,您使用 <xsl:for-each> 代替過程式語言中使用的 for 循環。清單 9 展示了具體的做法。

  清單 9. 使用 to 運算符實現 for 循環

<xsl:for-each select="1 to 32">
 <xsl:variable name="outerIndex" select="."/>
  <tr>
   <xsl:for-each select="1 to 5">

  這裡有一點復雜的部分需要處理。首先,在 XPath 1.0 和 XSLT 1.0 中,使用 <xsl:for-each> 為每次迭代更改上下文。例如,如果您使用 <xsl:for-each select="contestants/contestant>,上下文節點在每次迭代中是最新的 <contestant>。當您使用 to 運算符迭代各個整數時,上下文 item(2.0 中的上下文 item)是未定義的。如 清單 9 中所示,您需要保存外部 <xsl:for-each> 的當前值,因為它在內部 <xsl:for-each> 中不可用。

  但是這樣處理更加麻煩。如果上下文 item 未定義,您就無法從文檔中選擇節點。如果您知道您位於第 1 行第 1 列,就可以從 $seeds 序列中獲取第一個 item,因為 $seeds 是一個全局變量。這就告訴您需要找到 <contestant seed="1"> 元素。不幸的是,在文檔中您什麼也找不到。即使使用絕對的 XPath 表達式,如 /bracket/contestants/contestant[@seed='1'],也沒有辦法。因此,您需要將需要用到的節點存儲為全局變量。清單 10 向您展示了在需要時如何訪問全局變量。

  清單 10. 存儲所需節點的全局變量

<xsl:variable name="results" select="/bracket/results"/>
<xsl:variable name="contestants" select="/bracket/contestants"/>

  如果您需要獲取第 16 個 seed 競爭者的名稱,XPath 表達式為 $contestants/contestant[@seed="16"]。您使用類似的方法訪問 <result> 元素;如果您需要獲取第 2 回合中第二場比賽的獲勝者,表達式為 $results/result[@round="2"][2]/@winnerSeed。清單 11 給出了另外兩個您可以用來生成 Html 表的全局變量。

  清單 11. 其他有用的全局變量

<xsl:variable name="periods" as="xs:integer*">
 <xsl:sequence select="(4, 8, 16, 32, 64)"/>
</xsl:variable>
<xsl:variable name="backgroundColors" as="xs:string*">
 <xsl:sequence
  select="('background: #CCCCCC;', 'background: #9999CC;',
       'background: #99CCCC;', 'background: #CC99CC;',
       'background: #CCCC99;')"/>
</xsl:variable>

  這些變量存儲了每一列的 period 值和背景顏色。要使用每個變量,您只需將列號用作您所需值的索引即可。

  XPath 2.0 和 XSLT 2.0 中另一個不錯的增強是 <xsl:function> 元素。我們在樣式表中創建兩個函數:cellStyle 和 getResults。第一個函數返回每個單元格的邊界樣式,而第二個函數返回一場比賽結果(如果存在的話)。這兩個函數的參數是單元格的行號和列號。清單 12 給出了 cellStyle 函數的代碼。

  清單 12. cellStyle 函數

<xsl:function name="bracket:cellStyle" as="xs:string">
 <xsl:param name="row" as="xs:integer"/>
 <xsl:param name="column" as="xs:integer"/>
 
 <xsl:variable name="period" as="xs:integer"
  select="subsequence($periods, $column, 1)"/>
 <xsl:variable name="oneQuarter" as="xs:integer"
  select="$period div 4"/>
 <xsl:variable name="lastColumn" as="xs:boolean"
  select="$oneQuarter = count($contestants/contestant)"/>
 <xsl:variable name="position" select="$row mod $period"/>
 
 <xsl:value-of
  select="if ($position = $oneQuarter) then
        (if ($column = 1) then 'MatchupStart'
          else 'Solid')
      else if ($position = $period - $oneQuarter) then
           (if ($column = 1) then 'MatchupEnd'
             else 'Solid')
      else if ($lastColumn) then 'None'
      else if ($position &lt; $oneQuarter or
           $position &gt; $period - $oneQuarter) then 'None'
      else 'MatchupMiddle'"/>
</xsl:function>

  在您確定單元格樣式之前,需要使用四個變量。您需要從全局變量 $periods 中檢索 $period 的值。正如前面所提到的,$oneQuarter 就是 $period div 4。布爾值 $lastColumn 直接將 $oneQuarter 與錦標賽中競爭者的計數相比較。如果這些值相等,就處理上一列。最後,變量 $position 表示了模式中當前行的位置。換言之,對於第 1 和第 2 列,表中的第 9 行是重復組的第一行。使用模式中行的位置查看單元格的樣式。

  XPath 2.0 和 XSLT 2.0 中提供了一個 if 運算符,它帶有一個表達式(圓括號中),後接一個 then 和 else。所有這些內容都位於 <xsl:value-of> 的 select 屬性中。在本例中,您使用了一個表達式替代冗長的 <xsl:choose> 元素。此處的代碼很簡單,給定了表的公式;如果邏輯更復雜一些,則可能使用 <xsl:choose> 更易於維護,不過那需要編寫更多的代碼。

  關於 <xsl:function> 的一些語法注意點:首先,您需要為函數聲明新的名稱空間。如果在 XPath 表達式中調用 bracket:cellStyle(),名稱空間將告訴 XSLT 2.0 處理器如何查找函數。其次,注意,您使用 as="xs:string" 屬性表明此函數返回一個字符串。<xsl:value-of> 元素返回五個樣式名稱中的一個;即函數的輸出。

  getResults 函數稍微復雜一點,但不是很復雜,如 清單 13 所示。

  清單 13. getResults 函數

<xsl:function name="bracket:getResults" as="xs:string">
 <xsl:param name="row" as="xs:integer"/>
 <xsl:param name="column" as="xs:integer"/>
 
 <xsl:variable name="period" as="xs:integer"
  select="subsequence($periods, $column, 1)"/>
 <xsl:variable name="oneQuarter" as="xs:integer"
  select="$period div 4"/>
 <xsl:variable name="position" select="$row mod $period"/>
 
 <xsl:choose>
  <xsl:when test="$position = $oneQuarter
          or
          $position = $period - $oneQuarter">
   <xsl:variable name="round" select="$column - 1"/>
   <xsl:variable name="index"
    select="($row + $oneQuarter) div ($oneQuarter * 2)"/>
   <xsl:variable name="currentSeed"
    select="if ($column = 1) then
         subsequence($seeds, $index, 1)
        else
         $results/result[@round=$round][$index]/@winnerSeed"/>
   <xsl:choose>
    <xsl:when test="string-length(string($currentSeed))">
     <xsl:value-of
      select="concat('[', $currentSeed, '] ',
          $contestants/contestant[@seed=$currentSeed])"/>
    </xsl:when>
    <xsl:otherwise>
     <xsl:text>&#160;</xsl:text>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:when>
  <xsl:otherwise>
   <xsl:text>&#160;</xsl:text>
  </xsl:otherwise>
 </xsl:choose>
</xsl:function>

  使用的參數與以前相同。這裡需要對第 1 列做些特殊處理,該列中包含了來自 <contestant> 元素的數據,而其他列則包含了來自 <result> 元素的數據。與 cellStyle 函數一樣,您將計算 $period 和 $position,並使用 $oneQuarter 變量簡化公式。

  如果單元格中包含競爭者,則需要計算另外三個變量。$round 變量比列少一(第 2 列包含了第 1 回合的結果),您將根據先前討論的公式計算 $index 變量。

  您需要執行兩個步驟以查找適當的數據。首先,設置 $currentSeed 變量的值。如果是第 1 回合,則使用新的 subsequence 函數從 $seeds 變量中選擇一個值。對於其他回合,則從適當的 <result> 元素中獲取 winnerSeed 屬性。

  其次,除了第 1 列的處理方法不同以外,您還需要考慮 <result> 元素沒有獲勝者的情況(winnerSeed="")。如果出現這種情況,則返回一個未破壞的空間(&#160;)。XSLT 2.0 處理數據類型的方式(將來有文章會討論這個主題)要求您將 $currentSeed 轉換為一個字符串,然後測試字符串的長度。如果字符串的長度為零,則返回一個未破壞的空間,否則,返回 seed 和競爭者的名稱。

  最後要注意的是您在這裡使用了 <xsl:choose>。 雖然您可以使用一個 XPath if 語句實現任何操作,但是代碼顯得有些笨拙。雖然 <xsl:choose> 有些冗長,但代碼卻更加整潔、更易於理解。

  現在您已經設置了所需的全局變量和函數,樣式表的核心就顯得簡單優雅,如 清單 14 所示。

  清單 14. 樣式表的核心

<xsl:for-each select="1 to 32">
 <xsl:variable name="outerIndex" select="."/>
 <tr>
  <xsl:for-each select="1 to 5">
   <td style="{subsequence($backgroundColors, ., 1)}"
     class="{bracket:cellStyle($outerIndex, .)}">
    <xsl:value-of
     select="bracket:getResults($outerIndex, .)"/>
   </td>
  </xsl:for-each>
 </tr>
</xsl:for-each>

  您使用了兩個循環創建 32 行表的 5 列。對於每個單元格,背景色基於當前的列號。cellStyle 函數確定了邊界樣式(class="x"),而 getResults 函數確定了單元格的值。

  使用樣式表

  如您所料,您需要一個 XSLT 2.0 處理器使用此樣式表。這裡我不會重新打印整個樣式表,但是您必須使用 <xsl:stylesheet version="2.0"> 使處理器在 XSLT 2.0 模式下運行。我強烈建議使用 Michael Kay 的 Saxon 處理器(請參閱 參考資料 了解更多詳細信息)。Dr. Kay 是 XSLT 2.0 規范的編輯,而 Saxon 在開發規范時在某種程度上是一個測試用例。如果您使用 Saxon,則使用此命令通過樣式表 results-html.xsl 轉換 XML 文件 tourney.XML 並將輸出寫入 results.Html 文件:

  Java net.sf.saxon.Transform -o results.html tourney.XML results-Html.xsl

  要展示樣式表的靈活性,則獲取 XML 文件中的結果並運行轉換。清單 15 展示了 <result> 元素的外觀。

  清單 15. 帶有不完整錦標賽數據的 XML 文檔

<?XML version="1.0" encoding="UTF-8"?>
<!-- incomplete-tourney.XML -->
<bracket>
. . .
  <results>
   <result round="1" firstSeed="1" secondSeed="16" winnerSeed="1"/>
   <result round="1" firstSeed="8" secondSeed="9" winnerSeed="9"/>
   <result round="1" firstSeed="5" secondSeed="12" winnerSeed="5"/>
   <result round="1" firstSeed="4" secondSeed="13" winnerSeed="4"/>
   <result round="1" firstSeed="6" secondSeed="11" winnerSeed="11"/>
   <result round="1" firstSeed="3" secondSeed="14" winnerSeed="3"/>
   <result round="1" firstSeed="7" secondSeed="10" winnerSeed="10"/>
   <result round="1" firstSeed="2" secondSeed="15" winnerSeed="2"/>
   <result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
   <result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
   <result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
   <result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
   <result round="3" firstSeed="" secondSeed="" winnerSeed=""/>
   <result round="3" firstSeed="" secondSeed="" winnerSeed=""/>
   <result round="4" firstSeed="" secondSeed="" winnerSeed=""/>
  </results>
</bracket>   

  清單 15 表示第一回合後錦標賽的狀態。第 2、第 3 和第 4 回合的所有的 <result> 元素都有一個空的 winnerSeed 屬性。盡管如此,樣式表生成了正確的括弧,如 圖 3 所示。

  圖 3. 不完整錦標賽的括弧

使用 XPath 2.0 和 XSLT 2.0 節省開發時間並減少代碼量

  結束語

  本文展示了 XPath 2.0 和 XSLT 2.0 的很多新特性。在示例應用程序中,您獲取了一個笨拙的樣式表並將其重構為一段更小更易於維護的代碼。所做的部分工作用於分析代碼以查找模式,但是如果沒有 to 運算符、<xsl:function> 和 item 序列,簡化樣式表將非常困難。

  最重要的是,您創建了一些通用函數用於處理任意數量的競爭者。例如,要更改樣式表以處理 32 支隊伍參加的錦標賽,您需要更改 $seeds 和 $periods 的序列。您還需要使用 select="1 to $rounds" 替代 select="1 to 5",其中 $rounds 表示錦標賽的回合數。當然,最好的解決方案是創建 XSLT 2.0 函數,用於計算任何通用括弧的值,包括回合數、seed 序列(32 支隊伍的括弧特性匹配,1和 32、2 和 31 等等)和各列的 period。這個問題留給讀者做練習。

  問題的核心在於,您需要迭代表的 32 行,但是 XSLT 1.0 沒有提供實際的實現方法。XPath 2.0 和 XSLT 2.0 的新特性可以幫助您解決這個問題。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved