Vincent.Chan‘s Blog

          導(dǎo)航

          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          公告

          本博客僅為收集留待閱讀。在此對(duì)原作者表示感謝。
          ---------------------------

          留言簿(11)

          隨筆分類

          文章分類

          隨筆檔案

          文章檔案

          相冊(cè)

          閱讀排行榜

          評(píng)論排行榜

          常用鏈接

          統(tǒng)計(jì)

          積分與排名

          網(wǎng)站

          最新評(píng)論

          抽取界面::用 XML 和 XSL 構(gòu)建有良好適應(yīng)性的 Web 應(yīng)用前端

          級(jí)別: 初級(jí)

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

          2000 年 12 月 03 日

          使 用 XML 描述 Web 應(yīng)用用戶界面的部件可以使通過(guò) XSL 樣式表轉(zhuǎn)換用于多種設(shè)備的用戶界面變得簡(jiǎn)單。本文描述了使用 XML 數(shù)據(jù)和 XSL 樣式表來(lái)構(gòu)建復(fù)雜 Web 應(yīng)用的用戶界面。Web 日歷樣本應(yīng)用將演示基本的技術(shù)和概念。本文還包括超過(guò) 24 個(gè)的代碼樣本,您可以輕易擴(kuò)展這些樣本,以滿足特定需求。

          要充分理解本文,您應(yīng)該熟悉基于 HTTP 協(xié)議的 Web 應(yīng)用和 HTTP 請(qǐng)求-應(yīng)答機(jī)制。還應(yīng)該了解 Java 編程語(yǔ)言以及如何使用 Java Servlet 作為 Web 應(yīng)用的訪問(wèn)點(diǎn)。

          問(wèn)題

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

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





          回頁(yè)首


          樣本應(yīng)用

          為說(shuō)明和演示解決這個(gè)問(wèn)題的框架,我編寫了一個(gè)簡(jiǎn)單的、基于 Web 的日歷應(yīng)用,名為 WebCal。它維護(hù)多個(gè)用戶的日歷,并提供以下特性:

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

          圖 1 顯示登錄頁(yè)面的屏幕快照:


          圖 1. WebCal 登錄頁(yè)面
          性能圖像

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


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

          所有視圖都在藍(lán)框中顯示,該框架包含應(yīng)用標(biāo)題和徽標(biāo)、一些鏈接、主導(dǎo)航欄和一個(gè)子導(dǎo)航欄。

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

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

          WebCal 理解一系列請(qǐng)求類型,這些請(qǐng)求類型都使用自己的 HTTP 參數(shù)集。在樣本應(yīng)用中,通過(guò) Servlet 做所有的事:

          • 分析請(qǐng)求的 HTTP 參數(shù)
          • 執(zhí)行必需的操作
          • 生成應(yīng)答 XML
          • 將 XML 變換成 HTML(或任何其它格式)
          • 將應(yīng)答發(fā)回客戶機(jī)

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





          回頁(yè)首


          第 1 步. 基本 XML/XSL 框架

          首先,我們討論如何構(gòu)造和生成應(yīng)用所用的 XML 數(shù)據(jù)。然后,顯示 XSL 樣式表的通用結(jié)構(gòu),樣式表用來(lái)將 XML 數(shù)據(jù)變換成任何期望的輸出格式。

          構(gòu)造 XML 數(shù)據(jù)
          從 XML 到任何期望格式的 XSL 變換通過(guò)一系列對(duì) XML 數(shù)據(jù)中模式的匹配嘗試來(lái)進(jìn)行。應(yīng)該構(gòu)造 XML 數(shù)據(jù),使模式匹配盡可能簡(jiǎn)單。

          從應(yīng)用核心發(fā)送到前端的 XML 數(shù)據(jù)應(yīng)該只包含顯示和進(jìn)一步處理所必需的信息。首先,需要一個(gè)根元素,如清單 2 中所示。它應(yīng)該對(duì)所有視圖都是相同的,因?yàn)槟承╋@示部分也對(duì)所有視圖相同。如我們將看到的,通過(guò)匹配根元素,XSL 處理器可以最輕易地找到該信息。


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

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

          對(duì)于日歷視圖,需要包括有關(guān)要顯示時(shí)期的信息。其中包括這些天的名稱和確切日期,以及那段時(shí)期內(nèi)的所有活動(dòng)。既然該樣本演示應(yīng)用不在日歷視圖中顯示活動(dòng)描述,所以不需要在 XML 中包括它。因?yàn)橐獎(jiǎng)?chuàng)建至那些活動(dòng)的更詳細(xì)視圖的鏈接,所以,在 XML 中存儲(chǔ)每個(gè)活動(dòng)的活動(dòng)標(biāo)識(shí)。


          清單 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>

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

          用 Java 代碼還是 XSL 進(jìn)行計(jì)算?

          必須權(quán)衡用 XML 生成代碼和通過(guò) XSL 生成代碼之間的利弊。通常,當(dāng) XSL 計(jì)算過(guò)于復(fù)雜(例如將天組合成星期,以及生成用于在天、星期和月之間導(dǎo)航的 <next><previous> 元素)時(shí),最好用 Java 處理,并用 XML 反映結(jié)果。另外,即使在某些天中沒有活動(dòng),最好也為月份中的每一天生成一個(gè)標(biāo)記,而不要試著在 XSL 中實(shí)現(xiàn)循環(huán)(這很重要)。有關(guān) XSL 的詳細(xì)信息,請(qǐng)參閱 第 2 步

          在細(xì)節(jié)視圖中顯示活動(dòng)需要所有可用的活動(dòng)信息。對(duì)于 WebCal,這將與 清單 3 基本相同,只是添加了要以 <description> 元素出現(xiàn)的活動(dòng)描述。

          顯示要?jiǎng)?chuàng)建活動(dòng)的視圖不一定需要任何 XML 數(shù)據(jù),但是可以包括一個(gè) <event> 元素來(lái)提供缺省值。修改活動(dòng)再一次需要所有活動(dòng)數(shù)據(jù),以便用當(dāng)前值填充編輯表單的字段。

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

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


          清單 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>

          對(duì)星期視圖只特別生成了 <week> 元素,其余所有內(nèi)容都在所有視圖中相同,并可以保存在文件系統(tǒng)或數(shù)據(jù)庫(kù)中。

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

          用 Java 生成 XML
          要處理的數(shù)據(jù)駐留在服務(wù)器上。訪問(wèn)之后,需要將其格式化 XML。可以使用任何可用的 Java XML 包來(lái)生成包含應(yīng)答 XML 的 DOM 樹。下例引用 XML4J(請(qǐng)參閱 參考資源)。

          可以用 org.w3c.dom.Documentorg.w3c.dom.Element 實(shí)例的方式在 Java 中傳遞 XML 數(shù)據(jù)。在提供幾種有用的方法來(lái)分析所包含元素的語(yǔ)法方面,文檔接口具有一定的優(yōu)勢(shì)。


          清單 5. 用根節(jié)點(diǎn)創(chuàng)建文檔。
          												
          														
          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);

          您可能要編寫自己的實(shí)用程序方法來(lái)構(gòu)建應(yīng)用的 XML 文檔。可以使用可用的 XML 包中的語(yǔ)法分析器,以從流(包括文件)讀取 XML 或?qū)⑵鋵懭搿T谠创a中,查看 ShowAction.displayResult() 或任何其它 ShowAction 的 generateXML() 方法,以了解這是如何完成的。

          您可能還想?yún)⒖计渌? developerWorks 中關(guān)于 XML 和 Java 的文章,以獲得其它觀點(diǎn)(請(qǐng)參閱 參考資料)。

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

          通常以對(duì)源 XML 的引用和對(duì)要使用的樣式表的引用來(lái)初始化 XSL 處理器。作為變換的第一步,應(yīng)該給 XSL 處理器一個(gè)明確匹配源 XML 某一部分的模板。然后,將從那個(gè)通常匹配根節(jié)點(diǎn)的模板調(diào)用所有其它模板。這與調(diào)用所有其它子程序的“主例程”類似。


          清單 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>

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


          清單 10. ShowWeek.xsl (V.2):包括和調(diào)用生成框架的樣式表
          												
          														
          <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>

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


          清單 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 數(shù)據(jù)產(chǎn)生以下 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>

          選擇必要的樣式表
          如果輸出格式?jīng)]有 HTML 那樣復(fù)雜(例如 WML),則通常沒有必要對(duì)框架使用額外的樣式表。另外,如果視圖顯示非常類似的內(nèi)容,則它們可以共享樣式表。在 WebCal 中,雖然源 XML 略有不同,但是,用于創(chuàng)建和編輯活動(dòng)的視圖使用同一個(gè)樣式表。(有關(guān)代碼,請(qǐng)參閱 第 3 步)。

          但通常,當(dāng)使用這個(gè)框架時(shí),需要以下樣式表:

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

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

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


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

          用 Java 執(zhí)行變換
          WebCal 使用 James Clark 的 XT(請(qǐng)參閱 參考資料 )來(lái)將樣式表應(yīng)用到應(yīng)答 XML。如下所示,變換涉及到某些對(duì) XT 包的調(diào)用。其它象 LotusXSL 這樣的 XSL 處理器(請(qǐng)參閱 參考資料)以不同的方式對(duì)此進(jìn)行處理。

          WebCal 從 HTTP 請(qǐng)求檢索:

          • 瀏覽器類型
          • 瀏覽器理解的格式
          • 它所設(shè)置成的語(yǔ)言
          • 請(qǐng)求的操作

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

          總結(jié)第 1 步:得到的教訓(xùn)
          對(duì)于 XML 設(shè)計(jì)和 XML 生成:

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

          對(duì)于 XSL 樣式表的通用結(jié)構(gòu):

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




          回頁(yè)首


          第 2 步:用 XSLT 生成輸出

          前一節(jié)已經(jīng)講了輸出的基本部分,即 <html><head><body> 標(biāo)記。本節(jié)討論在 <body> 標(biāo)記內(nèi)部的 HTML 生成,哪一部分用框架的 XSL 完成,哪一部分用不同的 "main" 樣式表完成。

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


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

          使用 <xsl:apply-templates> 和 <xsl:for-each> 來(lái)處理 XML 元素
          知道框架的 XSL 樣式表如何訪問(wèn) XML 數(shù)據(jù)這一點(diǎn)很重要。對(duì)于左邊和頂部導(dǎo)航欄上的鏈接,可以使用 <xsl:apply-templates> 指令。請(qǐng)參閱 清單 17. 生成導(dǎo)航欄

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

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

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


          清單 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>


          可以有兩種類型的鏈接,即內(nèi)部鏈接和外部鏈接(如星期視圖的 XML 中所示(請(qǐng)參閱 清單 4 ))。 <link> 元素的 "type" 屬性定義類型。對(duì)于每一種鏈接類型,可以定義成自動(dòng)與正確的鏈接節(jié)點(diǎn)相匹配的模板。XML 屬性由 "@" 后面加上屬性名引用。必須求出 <xsl:template> 的 "match" 屬性中方括號(hào)中表達(dá)式的布兒值,以確定該模板是否與節(jié)點(diǎn)匹配。可以輕易生成外部鏈接,如下面的 "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. 應(yīng)用外部鏈接模板之后的 HTML
          												
          														
          <a class="..." >Home</a>

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


          清單 22. 從 XML 數(shù)據(jù)的主導(dǎo)航部分開始的外部鏈接
          												
          														
          <link type="
          internal" text="Day" href="ShowDay">

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


          清單 23. 與內(nèi)部鏈接匹配的 XSL 模板


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

          模板優(yōu)先

          在 XSL 中,具有更詳細(xì)匹配表達(dá)式的模板有優(yōu)先。XSL 規(guī)范(請(qǐng)參閱 參考資料)定義了所有的優(yōu)先規(guī)則。

          <link> 元素匹配的模板使用一個(gè) <xsl:variable> ,后者使用 <xsl:apply-templates> ,將與 <pass-param> 元素匹配的兩個(gè)模板中的一個(gè)應(yīng)用到當(dāng)前 <link> 元素的所有現(xiàn)有 <pass-param> 子代。然后,名為 "params" 的變量將包含那些模板應(yīng)用的結(jié)果。第一個(gè)與 <pass-param> 匹配的模板與所有 <pass-param> 元素匹配,第二個(gè)只與那些具有 "value" 屬性的 <pass-param> 元素匹配。在 XSL 中,規(guī)則越詳細(xì),其優(yōu)先越高,所以,第二個(gè)模板與具有 "value" 屬性的 <pass-param> 元素相匹配。

          有關(guān) XSL 如何處理優(yōu)先的提示,請(qǐng)參閱側(cè)欄 模板優(yōu)先

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

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

          變量和 XPath 表達(dá)式

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

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

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

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

          有關(guān)一些詳細(xì)信息,請(qǐng)參閱 變量和 XPath 表達(dá)式側(cè)欄。


          清單 25. 將當(dāng)前操作和對(duì)其元素的引用保存到 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. 獲得當(dāng)前操作名稱和相應(yīng) <action> 元素的 XSL

          生成子導(dǎo)航的部分將與 <sub-nav> 元素相匹配的模板應(yīng)用到與當(dāng)前操作相對(duì)應(yīng)的導(dǎo)航節(jié)點(diǎn)的 <sub-nav> 子代。對(duì)于 WebCal,該模板看起來(lái)與生成主導(dǎo)航欄的模板完全相同。

          主面板
          主面板由名為 "main" 的、且對(duì)每個(gè)視圖都不相同的模板生成。它從 HTML <div> 標(biāo)記中調(diào)用,該標(biāo)記限制主面板的尺寸,并且,如果需要的話,將顯示滾動(dòng)欄。將喜愛的尺寸作為命名的參數(shù)傳遞到 "main" 模板。

          在以下部分中,以星期視圖為例,演示如何將 XML 應(yīng)用數(shù)據(jù)變換成 HTML。


          圖 4. 使用嵌套表格來(lái)布置星期視圖
          性能圖像

          清單 27. 調(diào)用 "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" 模板中計(jì)算變量 main_widthmain_height 。寬度取決于標(biāo)志圖像的大小,而高度可以隨意按像素大小設(shè)置。

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

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


          清單 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 中不管用,因?yàn)?XSL 樣式表必須有良好的格式。這意味著:每個(gè)打開的標(biāo)記必須有一個(gè)相應(yīng)的結(jié)束標(biāo)記,而且不能部分嵌套元素(錯(cuò)誤: <a><b></a></b> )。紅色的 </tr> 標(biāo)記本想用作 <xsl:for-each> 之前的 <tr> 標(biāo)記的結(jié)束標(biāo)記。但是這可能意味著: <xsl:for-each><xsl:if> 元素與 <tr> 元素部分嵌套。XML 規(guī)范強(qiáng)制 XSL 處理器將最后一個(gè) </tr> 標(biāo)記解釋成 <xsl:for-each> 之前的 <tr> 標(biāo)記的結(jié)束標(biāo)記,并將兩個(gè)紅色標(biāo)記解釋成錯(cuò)誤(結(jié)束標(biāo)記沒有開始標(biāo)記,以及開始標(biāo)記沒有結(jié)束標(biāo)記)。

          對(duì)這個(gè)問(wèn)題的解決方案是在 XML 中枚舉天,然后使用匹配的表達(dá)式。在 WebCal 中,一星期中的每一天都有一個(gè)名為 "num" 、從 "1" 到 "7"的附加屬性。XSL 支持用布爾表達(dá)式比較數(shù)字,因此,可以使用 <xsl:apply-templates> 在兩個(gè) <tr> 元素內(nèi)實(shí)現(xiàn)兩個(gè)循環(huán),如清單 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> 的紅色選擇標(biāo)準(zhǔn)為 @num<=4@num>=5 。在 XSL 中,應(yīng)該始終轉(zhuǎn)義 <> 字符。然后,由 "day" 模板處理(也就是匹配)所有的 day 元素,(無(wú)論在第一個(gè)循環(huán)還是第二個(gè)循環(huán)中)。

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

          對(duì)于星期中天的名稱,可以將 "num" 屬性與 <xsl:choose> 語(yǔ)句一起使用,如清單 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" 屬性抽取活動(dòng)開始和結(jié)束時(shí)間。

          總結(jié)第 2 步

          第 2 步下了顯示了如何將 XML 變換成 HTML。通過(guò)使用上面顯示的技巧,還可以將 XML 變換成任何其它有良好形式的標(biāo)記語(yǔ)言,以支持多種設(shè)備。有一個(gè)對(duì)所有支持的客戶機(jī)格式都相同的中間格式,對(duì)把應(yīng)用邏輯從顯示邏輯分開會(huì)有所幫助。

          如需更詳細(xì)的說(shuō)明, XML Bible(請(qǐng)參閱 參考資料 )會(huì)給您有關(guān) XSL 和 XPath 的極好概述,包括數(shù)字轉(zhuǎn)換、比較和字符串操作。





          回頁(yè)首


          第 3 步:使用表單

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

          表單元素
          與所有其它 HTML 元素類似,可以通過(guò) XSL 模板生成表單元素,包括 <form> 元素,和說(shuō)明到應(yīng)用 Servlet 的哪個(gè)點(diǎn)的 "action" 屬性。每個(gè)表單都應(yīng)該有一個(gè)內(nèi)部操作,以在用戶提交表單之后處理表單。使用一個(gè)隱藏字段來(lái)調(diào)用特定的內(nèi)部操作,如清單 21 所示:


          清單 31. ShowEventFormMain.xsl - 用來(lái)顯示 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 瀏覽器的“后退”和“重新裝入”功能可能會(huì)導(dǎo)致某些表單處理混亂。要避免這種問(wèn)題,WebCal 使用兩種類型的操作: ShowActions (請(qǐng)參閱 ShowAction.java )使用 第 2 步 中所描述的 XSL 變換顯示視圖。 ProcessActions (請(qǐng)參閱 ProcessAction.java)在提交之時(shí)被調(diào)用,并且不顯示任何內(nèi)容,但使用 Java API HttpServletResponse.sendRedirect(String) 將客戶機(jī)瀏覽器 重定向"ShowAction"

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

          有關(guān)如何避免表單處理與瀏覽器“后退”和“重新裝入”按鈕沖突這個(gè)問(wèn)題的技巧,請(qǐng)參閱 表單處理側(cè)欄。

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





          回頁(yè)首


          結(jié)束語(yǔ)

          本文顯示了使用 XML 和 XSL 變換來(lái)創(chuàng)建可擴(kuò)展的 Web 應(yīng)用的一種方式。對(duì)應(yīng)用數(shù)據(jù)使用一種中間格式(從而將應(yīng)用邏輯與用戶界面分開)以及不將鏈接和其它導(dǎo)航信息硬編碼,可以實(shí)現(xiàn)高度可維護(hù)性。集成新功能性的新視圖只需對(duì)導(dǎo)航 XML 進(jìn)行一些更新,以及新建該視圖的 "main" 樣式表(每種支持的格式都需要一個(gè))即可。





          回頁(yè)首


          參考資料





          回頁(yè)首


          關(guān)于作者

          Martin Gerlach 目前在 IBM Almaden 研究中心的計(jì)算機(jī)科學(xué)部門進(jìn)行畢業(yè)后的研究工作。他持有漢堡應(yīng)用科學(xué)大學(xué)的計(jì)算機(jī)科學(xué)學(xué)位。可以通過(guò) mgerlac@almaden.ibm.com 與 Martin 聯(lián)系。

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

          主站蜘蛛池模板: 仙居县| 陆川县| 墨竹工卡县| 伊宁县| 克拉玛依市| 裕民县| 襄汾县| 深圳市| 温宿县| 登封市| 团风县| 嘉祥县| 泸溪县| 驻马店市| 新源县| 筠连县| 开江县| 黄石市| 松阳县| 巴里| 永昌县| 于田县| 莱州市| 永和县| 东兴市| 泗阳县| 榆林市| 新干县| 聂荣县| 北宁市| 新龙县| 达拉特旗| 大渡口区| 吉木萨尔县| 高唐县| 万州区| 梓潼县| 达州市| 绥江县| 昌吉市| 吉木萨尔县|