級(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ī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è)摘要、描述、開(kāi)始時(shí)間和結(jié)束時(shí)間。開(kāi)始時(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
|
第 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é)果。另外,即使在某些天中沒(méi)有活動(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 樣式表中使用它們。
以下代碼樣本顯示為從星期天開(kāi)始的、在“Martin
的生日聚會(huì)”之前的那個(gè)星期生成的完整
XML。它已準(zhǔn)備好變換成客戶機(jī)所喜愛(ài)的格式。
清單 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
樹(shù)。下例引用 XML4J(請(qǐng)參閱
參考資源)。
可以用
org.w3c.dom.Document
或
org.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),則通常沒(méi)有必要對(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 生成代碼分開(kāi)。
- 在生成 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>
將其裝入。
第 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)用的外觀和感覺(jué)的技術(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
然而,內(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/>
元素分開(kāi),而是由豎線分開(kāi))。已經(jīng)將該數(shù)據(jù)作為 HTTP 參數(shù)傳遞到
Servlet,并將其包括在 XML 的
<http-params>
部分。
清單 22. 從 XML 數(shù)據(jù)的主導(dǎo)航部分開(kāi)始的外部鏈接
<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)航欄的外觀和感覺(jué)是一致的,所以,不在框
架樣式表中創(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)欄。將喜愛(ài)的尺寸作為命名的參數(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_width
和
main_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
開(kāi)始,
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è)打開(kāi)的標(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)記沒(méi)有開(kāi)始標(biāo)記,以及開(kāi)始標(biāo)記沒(méi)有結(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)開(kāi)始和結(jié)束時(shí)間。
總結(jié)第 2 步
第 2 步下了顯示了如何將 XML 變換成
HTML。通過(guò)使用上面顯示的技巧,還可以將 XML
變換成任何其它有良好形式的標(biāo)記語(yǔ)言,以支持多種設(shè)備。有一個(gè)對(duì)所有支持的客戶機(jī)格式都相同的中間格式,對(duì)把應(yīng)用邏輯從顯示邏輯分開(kāi)會(huì)有所幫助。
如需更詳細(xì)的說(shuō)明,
XML Bible(請(qǐng)參閱
參考資料 )會(huì)給您有關(guān) XSL 和 XPath
的極好概述,包括數(shù)字轉(zhuǎn)換、比較和字符串操作。
第 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>
|
|
表單處理
使用常見(jiàn) 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
輸入字段和提交按鈕。
結(jié)束語(yǔ)
本文顯示了使用 XML 和 XSL 變換來(lái)創(chuàng)建可擴(kuò)展的 Web
應(yīng)用的一種方式。對(duì)應(yīng)用數(shù)據(jù)使用一種中間格式(從而將應(yīng)用邏輯與用戶界面分開(kāi))以及不將鏈接和其它導(dǎo)航信息硬編碼,可以實(shí)現(xiàn)高度可維護(hù)性。集成新功能性的新視圖只需對(duì)導(dǎo)航
XML 進(jìn)行一些更新,以及新建該視圖的 "main"
樣式表(每種支持的格式都需要一個(gè))即可。
參考資料
關(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)系。
|