Vincent.Chan‘s Blog

          常用鏈接

          統計

          積分與排名

          網站

          最新評論

          抽取界面::用 XML 和 XSL 構建有良好適應性的 Web 應用前端

          級別: 初級

          Martin Gerlach, 軟件工程師, IBM Almaden 研究中心

          2000 年 12 月 03 日

          使 用 XML 描述 Web 應用用戶界面的部件可以使通過 XSL 樣式表轉換用于多種設備的用戶界面變得簡單。本文描述了使用 XML 數據和 XSL 樣式表來構建復雜 Web 應用的用戶界面。Web 日歷樣本應用將演示基本的技術和概念。本文還包括超過 24 個的代碼樣本,您可以輕易擴展這些樣本,以滿足特定需求。

          要充分理解本文,您應該熟悉基于 HTTP 協議的 Web 應用和 HTTP 請求-應答機制。還應該了解 Java 編程語言以及如何使用 Java Servlet 作為 Web 應用的訪問點。

          問題

          比如,對于復雜的 Web 應用,您已經有一個想法(甚至曾經設計過)。您知道,如果既可以從標準的 Web 瀏覽器,又可以通過無線訪問協議 (WAP) 設備甚至可能是聲音來訪問 Web 應用,那么就可以獲得更多的利潤。您將把 XML 用于核心應用和處理用戶交互的服務器進程之間的通信。對于每種支持的客戶機格式,用戶界面將由所有可能的用戶操作的很多視圖組成,但是所有這些視圖將共享一個公共設計(即公司設計)和某些公共的隱藏信息。

          在如 HTML 這樣更復雜的輸出格式中,瀏覽器窗口中的大部分將始終或多或少地顯示相同的信息(如鏈接、圖像等)。既然計劃要使用 XSLT 來將由核心系統產生的 XML 變換成不同的客戶機格式,那么,可以認識到,對每種支持的客戶機格式只使用一個 XSL 樣式表是不切實際的。這將使維護工作繁重而困難。簡而言之,您需要一個有效的方式來組織 XML 數據和所有這些 XSL 樣式表。





          回頁首


          樣本應用

          為說明和演示解決這個問題的框架,我編寫了一個簡單的、基于 Web 的日歷應用,名為 WebCal。它維護多個用戶的日歷,并提供以下特性:

          • 新用戶可以通過注冊頁面注冊日歷。用戶提供個人信息,然后被分配系統用戶名和口令。
          • 已注冊用戶可以使用登錄頁面登錄到系統。
          • 登錄之后,用戶可以在其日歷(日歷視圖)的日、月和年視圖之間切換。
          • 用戶可以創建、查看、修改和刪除活動。
          • 活動有一個摘要、描述、開始時間和結束時間。開始時間、結束時間和摘要都顯示在日歷視圖中。

          圖 1 顯示登錄頁面的屏幕快照:


          圖 1. WebCal 登錄頁面
          性能圖像

          一旦成功登錄,用戶即可使用其日歷。典型的日歷視圖如 圖 2 所示。它顯示從 2001 年 2 月 11 日到 2001 年 2 月 17 日之間的那個星期。


          圖 2. WebCal 星期視圖
          性能圖像

          所有視圖都在藍框中顯示,該框架包含應用標題和徽標、一些鏈接、主導航欄和一個子導航欄。

          框架支持多個外部鏈接、內部視圖和內部操作的導航欄。主導航(位于徽標之下)提供從除登錄和注冊頁面之外的所有視圖至日歷視圖的鏈接。子導航欄允許內部操作,例如,在日歷視圖中創建活動以及在活動視圖中編輯或刪除活動。

          由于使用標準顯示尺寸(1024 x 768 或更大)的標準 Web 瀏覽器的 HTML 通常是最復雜的輸出,所以,WebCal 應用只演示用于 HTML 輸出的 XSL 樣式表。所有原則對其它基于 HTTP 的格式都相同。出于與用作 Web 服務器和 Servlet 引擎的 WebSphere 3.0 兼容的原因,WebCal 用 Java 1.1.7 編寫(請參閱 參考資料 )。它使用可以在任何 Servlet 引擎中運行的 Servlet。該 Servlet 使用瀏覽器 cookie(所有普通的基于 HTTP 的瀏覽器,包括 WAP 電話在內,都可以接受)來跟蹤用戶會話。

          WebCal 理解一系列請求類型,這些請求類型都使用自己的 HTTP 參數集。在樣本應用中,通過 Servlet 做所有的事:

          • 分析請求的 HTTP 參數
          • 執行必需的操作
          • 生成應答 XML
          • 將 XML 變換成 HTML(或任何其它格式)
          • 將應答發回客戶機

          清單 1. HTTP 請求示例
          												
          														
          http://localhost/webcal/WebCalServlet?action=ShowDay&date=2001-02-17





          回頁首


          第 1 步. 基本 XML/XSL 框架

          首先,我們討論如何構造和生成應用所用的 XML 數據。然后,顯示 XSL 樣式表的通用結構,樣式表用來將 XML 數據變換成任何期望的輸出格式。

          構造 XML 數據
          從 XML 到任何期望格式的 XSL 變換通過一系列對 XML 數據中模式的匹配嘗試來進行。應該構造 XML 數據,使模式匹配盡可能簡單。

          從應用核心發送到前端的 XML 數據應該只包含顯示和進一步處理所必需的信息。首先,需要一個根元素,如清單 2 中所示。它應該對所有視圖都是相同的,因為某些顯示部分也對所有視圖相同。如我們將看到的,通過匹配根元素,XSL 處理器可以最輕易地找到該信息。


          清單 2. 文檔頭和根元素
          												
          														
          <?xml version="1.0"?>
          <!DOCTYPE webcal>
          <webcal>
          <!--general stuff-->
          <!--view specific-->
          </webcal>

          既然頁面只是表單輸入的模板而且它們看起來總是相同的,所以,幾乎不需要任何其它處理或 XML 數據來顯示注冊和登錄頁面。

          對于日歷視圖,需要包括有關要顯示時期的信息。其中包括這些天的名稱和確切日期,以及那段時期內的所有活動。既然該樣本演示應用不在日歷視圖中顯示活動描述,所以不需要在 XML 中包括它。因為要創建至那些活動的更詳細視圖的鏈接,所以,在 XML 中存儲每個活動的活動標識。


          清單 3. 天的表示
          												
          														
          <webcal>
          ...
          <day date="2001-02-17">
          <event id="..." start="2001-02-17T20:00" end="2001-02-18T06:00">
          <summary>Martin's birthday party</summary>
          </event>
          </day>
          ...
          </webcal>

          顯示超過一天的視圖有幾個 <day> 元素。可以對這些元素排序,或以其出現的順序處理它們。為了排序,可以使用 date 屬性(以 ISO8601 的格式)。對于月視圖,可以將 <day> 元素組合成 <week> 標記。這在 XML 生成過程中不需要太多計算,并且可以簡化 XSL 處理。有關詳細信息,請查看 用 Java 代碼還是 XSL 進行計算?側欄。

          用 Java 代碼還是 XSL 進行計算?

          必須權衡用 XML 生成代碼和通過 XSL 生成代碼之間的利弊。通常,當 XSL 計算過于復雜(例如將天組合成星期,以及生成用于在天、星期和月之間導航的 <next><previous> 元素)時,最好用 Java 處理,并用 XML 反映結果。另外,即使在某些天中沒有活動,最好也為月份中的每一天生成一個標記,而不要試著在 XSL 中實現循環(這很重要)。有關 XSL 的詳細信息,請參閱 第 2 步

          在細節視圖中顯示活動需要所有可用的活動信息。對于 WebCal,這將與 清單 3 基本相同,只是添加了要以 <description> 元素出現的活動描述。

          顯示要創建活動的視圖不一定需要任何 XML 數據,但是可以包括一個 <event> 元素來提供缺省值。修改活動再一次需要所有活動數據,以便用當前值填充編輯表單的字段。

          當用戶登錄時,將需要所有視圖的某些信息(如框架的不同部分、鏈接和導航欄)。子導航根據視圖類型的不同而不同,但是,對于同一類型的兩個視圖,其子導航 是相同的(例如,顯示不同天的兩個天視圖)。用戶登錄之后,可能還要包括一些個人數據。示例應用只用到了名稱和用戶名稱。事實證明,包括當前視圖的 HTTP 參數也十分有用,這樣可以在 XSL 樣式表中使用它們。

          以下代碼樣本顯示為從星期天開始的、在“Martin 的生日聚會”之前的那個星期生成的完整 XML。它已準備好變換成客戶機所喜愛的格式。


          清單 4. 表示視圖的完整 XML
          												
          														
          <webcal>
          <http-params>
          <param name="action" value="ShowWeek"/>
          <param name="date" value="2001-02-11"/>
          </http-params>
          <user>
          <login>martin</login>
          <firstname>Martin</firstname>
          <middlename/>
          <lastname>Gerlach</lastname>
          </user>
          <navigation>
          <global>
          <main-nav>
          <link type="internal" text="Day" href="ShowDay">
          <pass-param name="date"/>
          </link>
          <link type="internal" text="Day" href="ShowWeek">
          <pass-param name="date"/>
          </link>
          <link type="internal" text="Day" href="ShowMonth">
          <pass-param name="date"/>
          </link>
          </main-nav>
          <links>
          <link type="internal" text="Logout" href="Logout"/>
          <link type="external" text="Home" />
          </links>
          </global>
          <actions>
          ...
          <action name="ShowWeek">
          <sub-nav>
          <link type="internal" text="New Event" href="ShowNewEvent">
          <pass-param name="date"/>
          </link>
          </sub-nav>
          </action>
          ...
          </actions>
          </navigation>
          <now date="2000-11-07T15:30"/>
          <week date="2001-02-11" previous="2001-02-04" next="2001-02-18">
          <day date="2001-02-11"/>
          <day date="2001-02-12"/>
          <day date="2001-02-13"/>
          <day date="2001-02-14"/>
          <day date="2001-02-15">
          <event id="(id1)" start="2001-02-15T18:00" end="2001-02-15T20:00">
          <summary>Shopping for Martin's birthday party</summary>
          </event>
          </day>
          <day date="2001-02-16"/>
          <day date="2001-02-17">
          <event id="(id2)" start="2001-02-17T20:00" end="2001-02-18T06:00">
          <summary>Martin's birthday party</summary>
          </event>
          </day>
          </week>
          </webcal>

          對星期視圖只特別生成了 <week> 元素,其余所有內容都在所有視圖中相同,并可以保存在文件系統或數據庫中。

          圖 2 顯示了清單 4 在屏幕上的顯示結果。(將在 第 2 步 中討論必要的 XSL 變換)。如果正在運行樣本應用,可以將 "&format=xml" 附加在當前 URL 之后,然后重新裝入該頁面,以查看當前視圖的應答 XML。

          用 Java 生成 XML
          要處理的數據駐留在服務器上。訪問之后,需要將其格式化 XML。可以使用任何可用的 Java XML 包來生成包含應答 XML 的 DOM 樹。下例引用 XML4J(請參閱 參考資源)。

          可以用 org.w3c.dom.Documentorg.w3c.dom.Element 實例的方式在 Java 中傳遞 XML 數據。在提供幾種有用的方法來分析所包含元素的語法方面,文檔接口具有一定的優勢。


          清單 5. 用根節點創建文檔。
          												
          														
          import org.w3c.dom.*; // DOM interfaces
          import org.apache.xerces.dom.*; // DOM implementation for XML4J

          ...
          Document responseXML = createDocument("webcal");

          public Document createDocument(String rootName)
          {
          Document doc = new DocumentImpl();
          doc.appendChild(doc.createElement(rootName));
          return doc;
          }



          清單 6. 獲得文檔根元素
          												
          														
          Element root = doc.getDocumentElement();



          清單 7. 將 <day date="2001-02-17"/>附加到元素之后
          												
          														
          Element dayElem = doc.createElement("day");
          dayElem.setAttribute("date","2001-02-17");
          someElement.appendChild(dayElem);



          清單 8. 將文本附加到元素 summaryElem: <summary>This is a summary</summary> 之后
          												
          														
          org.w3c.dom.Text textNode = doc.createTextNode("This is a summary");
          summaryElem.appendChild(textNode);

          您可能要編寫自己的實用程序方法來構建應用的 XML 文檔。可以使用可用的 XML 包中的語法分析器,以從流(包括文件)讀取 XML 或將其寫入。在源代碼中,查看 ShowAction.displayResult() 或任何其它 ShowAction 的 generateXML() 方法,以了解這是如何完成的。

          您可能還想參考其它 developerWorks 中關于 XML 和 Java 的文章,以獲得其它觀點(請參閱 參考資料)。

          構造 XSL 樣式表
          XSL 樣式表包含從一種 XML 格式到另一種 XML 格式(例如,任何有良好格式的標記語言)的變換規則。這些規則被編寫成所謂的模板,可以用兩種方式將它們應用到源 XML。一種是通過名稱從其它模板調用它們,另一種是自動將它們匹配到源 XML 的各個部分。轉換由 XSL 處理器完成。

          通常以對源 XML 的引用和對要使用的樣式表的引用來初始化 XSL 處理器。作為變換的第一步,應該給 XSL 處理器一個明確匹配源 XML 某一部分的模板。然后,將從那個通常匹配根節點的模板調用所有其它模板。這與調用所有其它子程序的“主例程”類似。


          清單 9. ShowWeek.xsl (V.1):用于 WebCal 星期視圖的 HTML 輸出的 XSL 模板
          												
          														
          <xsl:style sheet>
          <xsl:template match="webcal">
          <html>
          <head>
          <title>WebCal - Week</title>
          </head>
          <body>
          <!-- week view content -->
          </body>
          </html>
          </xsl:template>
          </xsl:style sheet>

          包括樣式表和通過名稱調用模板
          每個視圖都需要這樣的一個模板,但是,所有視圖的 <html><head><body> 元素都將相同。因此,如果抽取這些元素并將其放入公共模板,則可以從所有視圖調用該模板(通過名稱)。


          清單 10. ShowWeek.xsl (V.2):包括和調用生成框架的樣式表
          												
          														
          <xsl:style sheet>
          <xsl:include href="http://localhost/webcal/html/Frame.xsl"/>
          <xsl:template match="webcal">
          <xsl:call-template name="frame"/>
          </xsl:template>
          </xsl:style sheet>

          假設現在已經由所包括的樣式表生成了內容,那么,對每個視圖,還需要樣式表嗎?實際的視圖內容(在框架中)又在哪里生成呢?生成框架的模板需要調用另一個模板,即生成內容的模板。但是,框架如何知道應該是 哪個 模板(例如,生成的框架用于哪個視圖)呢?事實上,框架模板無需知道,它只需調用一個名為 "main" 的模板即可。"main" 模板在樣式表中定義。在包含與 "webcal" 匹配的模板的樣式表中包括該樣式表。這就是為什么需要對每個視圖使用一個樣式表的原因。以下是星期視圖的完整結構:


          清單 11. ShowWeek.xsl (V.3)
          												
          														
          <xsl:style sheet>
          <xsl:output method="html"/> <!-- for XT -->
          <xsl:include href="http://localhost/webcal/html/Frame.xsl"/>
          <xsl:include href="http://localhost/webcal/html/ShowWeekMain.xsl"/>
          <xsl:template match="webcal">
          <xsl:call-template name="frame"/>
          </xsl:template>
          </xsl:style sheet>



          清單 12. Frame.xsl
          												
          														
          <xsl:style sheet>
          <xsl:template name="frame">
          <html>
          <head>
          <title>
          WebCal -
          <xsl:value-of select="/webcal/http-params/param[@name='action']/@value"/>
          </title>
          </head>
          <body>
          <!-- do the frame -->
          <table>
          <xsl:comment>frame content</xsl:comment>
          ...
          <td>
          <div style="width: ...; height: ...; ...>
          <xsl:call-template name="main">
          <xsl:with-param name="width">?lt;/xsl:with-param>
          <xsl:with-param name="height">?lt;/xsl:with-param>
          </xsl:call-template>
          </div>
          </td>
          <xsl:comment>frame content continued</xsl:comment>
          ?
          </table>
          </body>
          </html>
          </xsl:template>
          </xsl:style sheet>



          清單 13. ShowWeekMain.xsl
          												
          														
          <xsl:style sheet>
          <xsl:template match="main">
          <xsl:param name="width"/>
          <xsl:param name="height"/>
          <xsl:comment>Week view content</xsl:comment>
          <!-- produce week view content here. Keep in mind this is called from
          inside a <div> element with the given width and height -->
          </xsl:template>
          </xsl:style sheet>

          清單 4 所示,所有這些從 XML 數據產生以下 HTML。


          清單 14. 星期視圖的 HTML
          												
          														
          <html>
          <head>
          <title>WebCal - ShowWeek</title>
          </head>
          <body>
          <table>
          <!--frame content-->
          ...
          <td>
          <div style="width: ...; height: ...; ...>
          <!--week view content-->
          ...
          </div>
          </td>
          <!--frame content continued-->
          ...
          </table>
          </body>
          </html>

          選擇必要的樣式表
          如果輸出格式沒有 HTML 那樣復雜(例如 WML),則通常沒有必要對框架使用額外的樣式表。另外,如果視圖顯示非常類似的內容,則它們可以共享樣式表。在 WebCal 中,雖然源 XML 略有不同,但是,用于創建和編輯活動的視圖使用同一個樣式表。(有關代碼,請參閱 第 3 步)。

          但通常,當使用這個框架時,需要以下樣式表:

          • 每個視圖和格式都需要一個根樣式表(如上面的 ShowWeek.xsl)
          • 每一種格式都需要一個框架樣式表
          • 每個視圖和格式都需要一個視圖內容樣式表,包含由 "frame" 模板調用的命名的模板。

          每個樣式表都位于自己的文件中。一個樣式表中可以有多個模板,這樣,"frame" 和 "main" 模板可以使用 <xsl:call-template><xsl:apply-templates> 將其它模板應用到應答 XML 的某些部分中。

          要想使用 HTTP 來包括如上所示的樣式表,所有樣式表都要位于本地 Web 服務器文檔目錄之下,如清單 15 中所示:


          清單 15. 本地 Web 服務器目錄
          												
          														
          /
          webcal/
          html/
          Frame.xsl
          LoginView.xsl
          LoginViewMain.xsl
          DayView.xsl
          DayViewMain.xsl
          WeekView.xsl
          WeekViewMain.xsl
          ...
          wml/
          Frame.xsl
          LoginView.xsl
          LoginViewMain.xsl
          ...

          用 Java 執行變換
          WebCal 使用 James Clark 的 XT(請參閱 參考資料 )來將樣式表應用到應答 XML。如下所示,變換涉及到某些對 XT 包的調用。其它象 LotusXSL 這樣的 XSL 處理器(請參閱 參考資料)以不同的方式對此進行處理。

          WebCal 從 HTTP 請求檢索:

          • 瀏覽器類型
          • 瀏覽器理解的格式
          • 它所設置成的語言
          • 請求的操作

          從所有這些信息,可以找到視圖的正確根 XSL 樣式表。請參閱 清單 16. 用 Java 進行 XSL 變換

          總結第 1 步:得到的教訓
          對于 XML 設計和 XML 生成:

          • 確定視圖所需的信息
          • 把為公共所需的信息而編寫的 XML 生成代碼與為視圖特別所需的信息而編寫的 XML 生成代碼分開。
          • 在生成 XML 之前或在其過程中,用 Java 代碼執行復雜計算,然后用 XML 反映結果(如果可能并且每種應答格式都需要的話)。
          • 使整個結構保持靈活。

          對于 XSL 樣式表的通用結構:

          • XSL 樣式表應該位于 Web 服務器的文檔目錄中,以允許用 <xsl:include> 將其裝入。




          回頁首


          第 2 步:用 XSLT 生成輸出

          前一節已經講了輸出的基本部分,即 <html><head><body> 標記。本節討論在 <body> 標記內部的 HTML 生成,哪一部分用框架的 XSL 完成,哪一部分用不同的 "main" 樣式表完成。

          框架
          表格是用來布置 HTML 頁面的好方式。WebCal 對框架使用一個表格,主要部分位于框架中間最大的表單元中。 圖 3 中的紅線顯示在 Frame.xsl 中用來繪制框架的表。除了中間的主要部分之外,所有事都由 Frame.xsl 完成。三個導航面板上的鏈接在每個視圖的 XML 數據中定義。


          圖 3. WebCal 用戶界面布局中的表格單元
          性能圖像

          使用 <xsl:apply-templates> 和 <xsl:for-each> 來處理 XML 元素
          知道框架的 XSL 樣式表如何訪問 XML 數據這一點很重要。對于左邊和頂部導航欄上的鏈接,可以使用 <xsl:apply-templates> 指令。請參閱 清單 17. 生成導航欄

          <xsl:apply-templates> 將對 XML 節點應用模板。這意味著:XSL 處理器查找與 <xsl:apply-tempates> 的 "select" 屬性定義的表達式相匹配的 XML 元素。對于邊鏈接的情況,將匹配導航部分中 <global> 元素下的所有 <links> 元素。如 清單 4 中星期視圖中的 XML 代碼所示,只有一個這樣的元素。它可以有任意數量的作為其子代的 <link> 元素。可以使用 <xsl:with-param> 來將任意數量的已命名的參數傳遞到匹配的模板。在本例中,它傳遞應該用于該鏈接的樣式名稱。

          本文不具體討論樣式、圖像以及其它這些可以改進 Web 應用的外觀和感覺的技術。看一下樣本應用中的源代碼,以了解如何在 XSL 中定義和使用 CSS 樣式。

          在 Frame.xsl 中還定義了與 <links> 元素匹配的模板。它使用 <xsl:for-each> 來迭代 <links><link> 子代。在該循環中,它應用與當前 <link> 元素(由 "." 表示當前元素)相匹配的模板,并傳遞 "css" 參數。在每個鏈接之后,它插入一個 <br/> 元素,以便在左邊,每行只有一個鏈接。


          清單 18. 鏈接
          												
          														
          <xsl:template match="links">
          <xsl:param name="css"/>

          <xsl:for-each select="link">
          <xsl:apply-templates select=".">
          <xsl:with-param name="css" select="$css"/>
          </xsl:apply-templates>
          <br/>
          </xsl:for-each>
          </xsl:template>


          可以有兩種類型的鏈接,即內部鏈接和外部鏈接(如星期視圖的 XML 中所示(請參閱 清單 4 ))。 <link> 元素的 "type" 屬性定義類型。對于每一種鏈接類型,可以定義成自動與正確的鏈接節點相匹配的模板。XML 屬性由 "@" 后面加上屬性名引用。必須求出 <xsl:template> 的 "match" 屬性中方括號中表達式的布兒值,以確定該模板是否與節點匹配。可以輕易生成外部鏈接,如下面的 "home" 鏈接所示:


          清單 19. XML 中的外部鏈接
          												
          														
          <link type="
          external" text="Home"/>




          清單 20. 與外部鏈接匹配的 XSL 模板
          												
          														
          <xsl:template match="link
          [@type='external']">
          <xsl:param name="css"/>
          <a class="{$css}" href="{@href}"><xsl:value-of select="@text"/></a>
          </xsl:template>




          清單 21. 應用外部鏈接模板之后的 HTML
          												
          														
          <a class="..." >Home</a>

          然而,內部鏈接卻鏈接到 WebCalServet,并可以有任意數量的、必須轉換成 HTTP 參數的 <pass-param> 子代。這略為復雜,并要求在 XML 數據中進行一些導航。以下鏈接從 XML 數據的 <main-nav> 部分獲得。它將顯示在頂部導航欄上,指向當前日期的天視圖。( <main-nav> 元素由與 <links> 元素上所用模板類似的模板處理 。唯一區別是:該鏈接不由 <br/> 元素分開,而是由豎線分開)。已經將該數據作為 HTTP 參數傳遞到 Servlet,并將其包括在 XML 的 <http-params> 部分。


          清單 22. 從 XML 數據的主導航部分開始的外部鏈接
          												
          														
          <link type="
          internal" text="Day" href="ShowDay">

          <pass-param name="date"/>
          </link>


          清單 23. 與內部鏈接匹配的 XSL 模板


          清單 24. 經過 XSL 處理之后的 HTML
          												
          														
          <a class="..." href="/webcal/WebCalServlet?action=ShowDay&date=...">Day</a>

          模板優先

          在 XSL 中,具有更詳細匹配表達式的模板有優先。XSL 規范(請參閱 參考資料)定義了所有的優先規則。

          <link> 元素匹配的模板使用一個 <xsl:variable> ,后者使用 <xsl:apply-templates> ,將與 <pass-param> 元素匹配的兩個模板中的一個應用到當前 <link> 元素的所有現有 <pass-param> 子代。然后,名為 "params" 的變量將包含那些模板應用的結果。第一個與 <pass-param> 匹配的模板與所有 <pass-param> 元素匹配,第二個只與那些具有 "value" 屬性的 <pass-param> 元素匹配。在 XSL 中,規則越詳細,其優先越高,所以,第二個模板與具有 "value" 屬性的 <pass-param> 元素相匹配。

          有關 XSL 如何處理優先的提示,請參閱側欄 模板優先

          復雜的 XPath 表達式
          第一個模板使用 XPath 表達式從 <http-params> 部分查詢值。可以將表達式 "/webcal/http-params/param[@name=$pname]/@value" 解釋成:“獲得 'name' 屬性值為變量 'pname' 中所定義值的 <webcal> 元素之下的 <http-params> 元素的 <param> 元素的名為 'value' 的屬性值。有了該值之后,模板構建一個如 "&name=value" 這樣的字符串。必須象 HTML 那樣,用 & 實體將 & 符號轉義。使用類似的表達式來從 <head> 部分中的 action HTTP 參數生成頁面標題。(請參閱 清單 12)。

          第二個模板只使用表達式 @value 來引用當前元素的 "value" 屬性,其中,當前指的是與模板匹配的 <pass-param> 元素。然后,模板再次從這個值和 "name" 屬性生成一個字符串 "&name=value"

          變量和 XPath 表達式

          可以在 XSL 元素的屬性中(例如,在 <xsl:template> 的 "match" 屬性中,或者在 <xsl:value-of><xsl:apply-templates> 等的 "select" 屬性中)使用 XPath 表達式和對 XSL 變量的引用。也可以在非 XSL 元素的屬性(如 <a> 的 "href" 屬性)中使用它們。在這種情況下,需要將表達式放在花括號中: "{/webcal/now}" <now> 元素的值)。

          在與外部鏈接匹配的模板中,變量 "params" 包含從 <pass-param> 元素生成的所有字符串的并置。然后將該并置包括在生成的 <a> element ( href="...{$params}" 的 "href" 屬性中)。

          將 XPath 表達式結果保存在 XSL 變量中的更多內容
          框架的底部導航欄包含一些不固定的鏈接,它們依賴于當前視圖。日歷視圖的底部導航欄包含一個導向用戶可以創建新活動的頁面的鏈接。創建活動之后,將該活動 作為到活動細節視圖的鏈接顯示在日歷中。在那個視圖中,子導航欄包含后退、編輯活動和刪除活動的鏈接。但是,子導航欄的外觀和感覺是一致的,所以,不在框 架樣式表中創建它。

          XML 數據的 <navigation> 部分以 <link> 元素的形式包含有關每個視圖的導航欄內容的信息。要訪問該信息,"frame" 模板需要知道顯示的是哪一個視圖。XML 數據的 <http-params> 部分包含 action 參數的值。清單 25 顯示了如何保存當前操作以及到兩個 XSL 變量的相應 <action> 元素的引用。然后,使用到 <action> 元素的引用來生成子導航欄。

          有關一些詳細信息,請參閱 變量和 XPath 表達式側欄。


          清單 25. 將當前操作和對其元素的引用保存到 XML 變量
          												
          														
          <webcal>
          <http-params>
          <param name="action" value="ShowWeek"/>
          ...
          <navigation>
          ...
          <actions>
          ...
          <action name="ShowWeek">
          <link type="
          internal" text="New Event" href="ShowNewEvent">

          <pass-param name="date"/>
          </link>
          </sub-nav>
          </action>
          ...
          </webcal>


          清單 26. 獲得當前操作名稱和相應 <action> 元素的 XSL

          生成子導航的部分將與 <sub-nav> 元素相匹配的模板應用到與當前操作相對應的導航節點的 <sub-nav> 子代。對于 WebCal,該模板看起來與生成主導航欄的模板完全相同。

          主面板
          主面板由名為 "main" 的、且對每個視圖都不相同的模板生成。它從 HTML <div> 標記中調用,該標記限制主面板的尺寸,并且,如果需要的話,將顯示滾動欄。將喜愛的尺寸作為命名的參數傳遞到 "main" 模板。

          在以下部分中,以星期視圖為例,演示如何將 XML 應用數據變換成 HTML。


          圖 4. 使用嵌套表格來布置星期視圖
          性能圖像

          清單 27. 調用 "main" 模板
          												
          														
          <xsl:template name="frame">
          ...
          <td class="main" align="left" valign="top"
          width="{$main_width}" height="{$main_height}">
          <div style="...
          width:{$main_width}px; height:{$main_height}px; overflow: auto; ...">
          <xsl:call-template name="main">

          <xsl:with-param name="width" select="$main_width"/>
          <xsl:with-param name="height" select="$main_height"/>
          </xsl:call-template>
          </div>
          </td>
          ...
          </xsl:tempate>


          在 "frame" 模板中計算變量 main_widthmain_height 。寬度取決于標志圖像的大小,而高度可以隨意按像素大小設置。

          在 "main" 模板中,使用與框架中所用的類似的技巧來顯示內容。首先,看一下 Show...Main.xsl 文件,以了解如何布置頁面。雖然有幾個特殊的 XSL 特性,但是,我將在下一節中解釋。

          使用數字
          在星期視圖中,將星期分成兩行。既然這只用于 HTML(您可能不想在其它設備中使用類似的顯示),所以在 XSL 樣式表中分成兩行。您可能很想如清單 28 那樣將 <xsl:for-each><xsl:if>position() 函數一起使用。從 1 開始, position() 返回 for-each 迭代的順序號。


          清單 28. 將星期分成兩行,嘗試 1
          												
          														
          <xsl:template name="main">
          ...
          <table>
          <tr>
          <!-- title --> ...
          </tr>
          <tr>
          <xsl:for-each select="webcal/week/day">
          <!-- do the day --> ...
          <xsl:if test="position()=4">
          <!-- next row -->

          </tr>
          <tr>
          </xsl:if>
          </xsl:for-each>
          </tr>
          </table>
          </xsl:tempate>


          這在 XSL 中不管用,因為 XSL 樣式表必須有良好的格式。這意味著:每個打開的標記必須有一個相應的結束標記,而且不能部分嵌套元素(錯誤: <a><b></a></b> )。紅色的 </tr> 標記本想用作 <xsl:for-each> 之前的 <tr> 標記的結束標記。但是這可能意味著: <xsl:for-each><xsl:if> 元素與 <tr> 元素部分嵌套。XML 規范強制 XSL 處理器將最后一個 </tr> 標記解釋成 <xsl:for-each> 之前的 <tr> 標記的結束標記,并將兩個紅色標記解釋成錯誤(結束標記沒有開始標記,以及開始標記沒有結束標記)。

          對這個問題的解決方案是在 XML 中枚舉天,然后使用匹配的表達式。在 WebCal 中,一星期中的每一天都有一個名為 "num" 、從 "1" 到 "7"的附加屬性。XSL 支持用布爾表達式比較數字,因此,可以使用 <xsl:apply-templates> 在兩個 <tr> 元素內實現兩個循環,如清單 29 所示:


          清單 29. 將星期分成兩行,嘗試 2
          												
          														
          <xsl:template name="main">
          <table>
          <tr>
          <!-- title --> ...
          </tr>
          <tr valign="top">
          <xsl:apply-templates select="/webcal/week/day
          [@num<=4]"/>
          </tr>
          <tr valign="top">
          <xsl:apply-templates select="/webcal/week/day
          [@num>=5]"/>
          </tr>
          </table>
          </xsl:tempate>

          <xsl:tamplate match="day">
          <!-- this matches all day elements -->
          <!-- do the day here --> ...
          </xsl:template>


          <xsl:apply-templates> 的紅色選擇標準為 @num<=4@num>=5 。在 XSL 中,應該始終轉義 <> 字符。然后,由 "day" 模板處理(也就是匹配)所有的 day 元素,(無論在第一個循環還是第二個循環中)。

          另一個問題是為每一天生成標題。XML 的 day 元素有一個名為 "date" 的屬性。其格式為 "yyyy-mm-dd"(ISO 標準化的格式)。那么,怎樣才能將它變換成 "day mm-dd" 的格式呢?對于 "mm-dd" 部分,XSL 提供字符串操作。通過使用 substring-after()substring-before() 或二者的組合,可以分析字符串的語法。

          對于星期中天的名稱,可以將 "num" 屬性與 <xsl:choose> 語句一起使用,如清單 30 所示。


          清單 30. <xsl:choose> 和字符串操作
          												
          														
          <xsl:template match="day">
          <table>
          <tr>
          <td class="maininverse"...>

          <xsl:choose>
          <xsl:when test="@num=1">Sun
          </xsl:when>
          <xsl:when test="@num=2">Mon</xsl:when>
          ...

          </xsl:choose>
          <xsl:value-of select="' '"/><!-- this generates a space -->
          <a class="maininverse" href="/webcal/WebCalServlet?action=ShowDay&date={@date}">
          <xsl:value-of select="
          substring-after(@date,'-')"/>
          </a>
          </td>
          </tr>
          <!-- process events here --> ...
          </table>
          </xsl:template>


          該字符串操作還從 <event> 元素的 "start" 和 "end" 屬性抽取活動開始和結束時間。

          總結第 2 步

          第 2 步下了顯示了如何將 XML 變換成 HTML。通過使用上面顯示的技巧,還可以將 XML 變換成任何其它有良好形式的標記語言,以支持多種設備。有一個對所有支持的客戶機格式都相同的中間格式,對把應用邏輯從顯示邏輯分開會有所幫助。

          如需更詳細的說明, XML Bible(請參閱 參考資料 )會給您有關 XSL 和 XPath 的極好概述,包括數字轉換、比較和字符串操作。





          回頁首


          第 3 步:使用表單

          這一步解釋如何在第 1 步和第 2 步所描述的 XSL 框架中使用表單。當然,前提是客戶機要支持表單,但是幾乎所有可用的基于 HTTP 的瀏覽器都支持表單。本節顯示如何在 WebCal 中使用 XSL 來生成 HTML 表單來編輯和創建日歷活動(例如,會議和約會)。示例還顯示了該操作如何共享一個公共樣式表。

          表單元素
          與所有其它 HTML 元素類似,可以通過 XSL 模板生成表單元素,包括 <form> 元素,和說明到應用 Servlet 的哪個點的 "action" 屬性。每個表單都應該有一個內部操作,以在用戶提交表單之后處理表單。使用一個隱藏字段來調用特定的內部操作,如清單 21 所示:


          清單 31. ShowEventFormMain.xsl - 用來顯示 new event 或 edit event 表單
          												
          														
          <xsl:template name="main">
          ...
          <form method="post"
          action="/webcal/WebCalServlet">
          <!-- 'edit' is an XSL variable. It is set to 'true' if the action was 'ShowEditEvent'.-->
          <xsl:if test="
          $edit='true'">

          <input type="hidden" name="action" value="ProcessEditEvent"/>
          <input type="hidden" name="id" value="{/webcal/event/@id}"/>
          </xsl:if>
          <xsl:if test="
          $edit='false'">

          <input type="hidden" name="action" value="ProcessNewEvent"/>
          </xsl:if>
          ...
          <!-- build the form -->
          ...
          </form>
          </xsl:template>


          表單處理

          使用常見 Web 瀏覽器的“后退”和“重新裝入”功能可能會導致某些表單處理混亂。要避免這種問題,WebCal 使用兩種類型的操作: ShowActions (請參閱 ShowAction.java )使用 第 2 步 中所描述的 XSL 變換顯示視圖。 ProcessActions (請參閱 ProcessAction.java)在提交之時被調用,并且不顯示任何內容,但使用 Java API HttpServletResponse.sendRedirect(String) 將客戶機瀏覽器 重定向"ShowAction"

          在表單中,可以使用客戶機格式中的所有輸入元素。WebCal 正好在登錄、注冊和顯示/編輯活動表單上使用文本輸入字段。輸入字段的名稱將是在提交表單時調用的 POST 操作的 HTTP 參數。Servlet 可以通過 Java API HttpServletRequest.getParameter(String) 獲得它們。

          有關如何避免表單處理與瀏覽器“后退”和“重新裝入”按鈕沖突這個問題的技巧,請參閱 表單處理側欄。

          清單 32. 生成表單元素 顯示了 XSL 模板如何生成 HTML 輸入字段和提交按鈕。





          回頁首


          結束語

          本文顯示了使用 XML 和 XSL 變換來創建可擴展的 Web 應用的一種方式。對應用數據使用一種中間格式(從而將應用邏輯與用戶界面分開)以及不將鏈接和其它導航信息硬編碼,可以實現高度可維護性。集成新功能性的新視圖只需對導航 XML 進行一些更新,以及新建該視圖的 "main" 樣式表(每種支持的格式都需要一個)即可。





          回頁首


          參考資料





          回頁首


          關于作者

          Martin Gerlach 目前在 IBM Almaden 研究中心的計算機科學部門進行畢業后的研究工作。他持有漢堡應用科學大學的計算機科學學位。可以通過 mgerlac@almaden.ibm.com 與 Martin 聯系。

          posted on 2006-03-21 23:28 Vincent.Chen 閱讀(342) 評論(0)  編輯  收藏 所屬分類: XML

          主站蜘蛛池模板: 衢州市| 米泉市| 新民市| 藁城市| 曲松县| 武汉市| 苍南县| 沅江市| 合阳县| 朝阳区| 平和县| 巍山| 武清区| 泽州县| 怀化市| 荣昌县| 高密市| 响水县| 全南县| 汪清县| 英吉沙县| 西和县| 崇左市| 安陆市| 宿州市| 潍坊市| 察雅县| 沂源县| 囊谦县| 阜宁县| 博罗县| 中阳县| 蒙山县| 金湖县| 新野县| 揭西县| 南澳县| 崇州市| 江油市| 犍为县| 双牌县|