DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 在運行時將數據與XSLT樣式表集成
在運行時將數據與XSLT樣式表集成
編輯:XML詳解     

本文描述了一種用於進行下列操作的機制:分別開發應用程序業務數據和表示數據,然後使用通用的 XSLT 樣式表將它們組合在一起。應用程序業務數據被格式化成 XML 形式,而表示數據可以用傳統工具來創建。附加屬性增強了表示數據,樣式表使用這些屬性來將表示細節應用於實際內容。對於最終格式的創建,不需要專門的編程,因為這個過程是由 XSLT 處理器完成的。

  為最充分地理解本文,您應該了解 XML 和 XSLT 的基礎知識。請參閱 參考資料一節,以獲得至可作為這些主題介紹的教程和文章的鏈接。這裡顯示的所有示例都是使用 Apache Xerces XML 解析器,版本 2.0.1 和 Apache Xalan XSLT 處理器,版本 2.3.1(請參閱 參考資料中的 apache XML Project)產生的。

  應用程序體系結構

  對於應用程序體系結構,我選擇了 Java 2 企業版(J2EE)提議的 n 層體系結構,如圖 1 所示。

  圖 1. n 層體系結構

在運行時將數據與XSLT樣式表集成

  這裡,我將集中討論第 1 層和第 2 層。如果您期望用戶輸出由 servlet 處理,則可以改進這個體系結構。這個 servlet 調用與後端通信的應用程序業務邏輯。這個後端可以是(例如)存在於網絡中獨立服務器上的 Enterprise JavaBean。可以假設所有業務數據都被格式化成 XML。然後,通過使用 Java Server Page(JSP)准備該數據以進行表示,並將它發回客戶機,如圖 2 所示。

  圖 2. 使用 JSP 准備用於表示的數據

在運行時將數據與XSLT樣式表集成


 

如果將該圖片轉換成“模型-視圖-控制器(Model-VIEw-Controller (MVC))”設計模式上,則 servlet 充當控制器角色,應用程序邏輯代表模型,JSP 是視圖元素。(誠然,這種分配是簡化了的,但它顯示了基本原理。)

  准備用於表示的應用程序數據

  為了充分利用使用 XML 創建應用程序數據的好處,必須先將數據轉換成可表示的格式。做到這一點的常用方法是 XSLT 樣式表。在 XSL 規范(另見 參考資料中的可擴展樣式表語言(eXtensible Stylesheets Language))中有兩種可能的方法。您可以:

  使用所謂的 格式化對象(FO)

  創建簡單的 XSLT 樣式表

  FO 通常創建二進制輸出格式,但至今仍未被廣泛使用(即使它們更專用於編輯表示數據)。另外,常用的 XSL 處理器和工具幾乎也不支持它們。而另一方面,XSLT 樣式表被更廣泛地使用,所以本文中我將集中討論 XSLT 樣式表。

  XSLT 樣式表處理結構

  XSLT 樣式表始終使用一個 XML 文檔來充當其輸入。XSL 處理器根據樣式表中的規則來處理這個輸入文檔。這會產生一個新的 XML 輸出文檔,如圖 3 所示。

  圖 3. 使用 XSLT 樣式表來處理 XML 文檔

在運行時將數據與XSLT樣式表集成

  在大多數情況下,這種結構會導致樣式表中規則和表示數據的混合。我假設我有 XML 格式的地址數據,需要將這些數據轉換成 HTML,以在浏覽器中表示。清單 1 顯示了 XML 輸入數據。注:XSLT 樣式表處理步驟的輸出是另一個 XML 文檔。HTML 不一定是格式良好的,所以這裡將始終產生 XHTML(XHtml 一定始終是格式良好)。稍後再討論這個問題。

清單 1. XML 輸入數據

<addressbook>
   <address>
     <addressee>John Smith</addressee>
     <streetaddress>250 18th Ave SE</streetaddress>
     <city>Rochester</city>
     <state>MN</state>
     <postalCode>55902</postalCode>
   </address>
   <address>
     <addressee>Bill Morris</addressee>
     <streetaddress>1234 Center Lane NW</streetaddress>
     <city>St. Paul</city>
     <state>MN</state>
     <postalCode>55123</postalCode>
   </address>
</addressbook>

  清單 2 顯示了將該數據轉換成 Html 格式的 XSLT 樣式表。(請參閱 使用 Apache Xalan 來處理用 Java 編寫的 XSLT 樣式表,以查看使用 apache Xalan XSLT 處理器對給定的 XML 文檔運行 XSLT 樣式表的 Java 樣本程序。)

  清單 2. 使用 XSLT 樣式表來將數據轉換成 Html

<?XML version='1.0'?>
<xsl:stylesheet XMLns:xsl="<http://www.w3.org/1999/XSL/Transform>"
  version="1.0">
 <xsl:output method="Html" indent="yes"/>
<!-- copy all elements that are not matched otherwise -->
<xsl:template match="* | @*">
<xsl:copy><xsl:copy-of select="@*"/><xsl:apply-templates/></xsl:copy>
</xsl:template>
<xsl:template match="addressbook">
   <Html>
   <head><title>My Addressbook</title></head>
   <body>
   <h1>My Addressbook</h1>
     <table border="1">
        <thead>
        <tr>
          <td>Name</td>
          <td>Street</td>
          <td>City</td>
          <td>Postal Code</td>
        </tr>
        </thead>
        <tbody>
        <xsl:for-each select="address">
          <tr>
          <td><xsl:value-of select="addressee"/></td>
          <td><xsl:value-of select="streetaddress"/></td>
          <td><xsl:value-of select="city"/></td>
          <td><xsl:value-of select="postalCode"/></td>
          </tr>
        </xsl:for-each>
        </tbody>
     </table>
   </body>
   </Html>
</xsl:template>
</xsl:stylesheet>


 

 清單 2 揭示了這樣一個問題:樣式表包含有關業務數據和表示數據本身的轉換信息,從而使得幾乎不可能使用 HTML 編輯環境。另外,需要為每種輸出格式復制樣式表。例如,如果客戶機設備是移動電話,則可以將表示數據格式化成 WML,而不是 Html。

  使用 apache Xalan 來處理用 Java 編寫的 XSLT 樣式表

  如果您要試驗本文中給出的一些示例,則可以考慮開發利用 Apache Xalan XSLT 處理器的 Java 應用程序。這個 XSLT 引擎基於 apache Xerces XML 解析器,它支持 Java API for XML Processing(JAXP)版本 1.1。JAXP 1.1 規范定義一個名為 javax.XML.transform.Transformer 的接口,它是應用程序用來執行實際轉換的實例。Transformer 實例是通過 Javax.XML.transform.TransformerFactory 創建的。

  DoTransform 樣本類將兩個文件名作為輸入 ― 一個用於輸入 XML 文件,另一個用於包含 XSLT 樣式表的文件。

  注:因為使用標准的 Java API,所以可以在任何符合 XSLT 的處理實現上運行該代碼。

  將表示數據與樣式表分開

  讓我們撇開樣式表,單獨地描述表示數據。為了表示數據和表示元素之間的關聯,可以為所使用的 Html 標記定義擴展。然後,可以創建一個使用這些擴展的樣式表來創建最終的輸出數據。

  一個問題是 XSLT 處理器只處理一個 XML 輸入文檔,然後使用樣式表創建一個 XML 輸出文檔。(注:XML 文檔可以以各種形式傳遞給 XSLT 處理器:作為文件、作為字節流或作為實際的 DOM 對象。)然而,在本例中,想要有兩個輸入源:應用程序數據和表示數據。可以用兩種方法解決這個問題:

  使用 XSL 中的 document() 函數,嵌入一個附加的 XML 文件以供樣式表使用。


 

通過編程將輸入源合並到一個 XML 文檔,這樣就向 XSLT 處理器提供了一個輸入源。

  兩個選項都是有效的。我將在這裡使用第二個選項,它會產生圖 4 中演示的結構。

  圖 4. 將多個輸入源合並到一個 XML 文檔

在運行時將數據與XSLT樣式表集成

  請注意,按照 XSLT 處理器的要求,這裡的顯示數據必須以格式良好的 XML 格式存在,這一點很重要。HTML 並不是在所有情況下都是格式良好的,然而可以合並 XHTML 標准來彌補了這一缺陷。我不打算詳細討論這個主題,而只提供一個示例,讓我們看一下 Html 中的 <br> 元素。通常,這個元素被用做空元素,但並沒有按 XML 標准要求的那樣來結束它。在格式良好的 XML 中,該元素必須寫成 <br /> (雖然最常用的浏覽器同時允許這兩種用法)。

  擴展 Html 元素

  要將數據轉換成期望的輸出格式,需要知道哪個表示元素與哪個數據元素相關聯。可以通過將屬性添加到 Html 元素來完成這一任務,這允許樣式表處理數據綁定,如下面的清單 3 所示。

  清單 3. 將屬性添加到 Html 元素

<?XML version="1.0"?>
<combinedelement>
<vIEw>
 <Html>
  <head>
  <title>My Addressbook</title>
  </head>
  <body>
   <form method="POST">
   <table id="AddressesPanel" dataSrc="addressbook"
    rowelement="address">
   <tr>
          <th>Name</th>
          <th>Street</th>
          <th>City</th>
          <th>Postal Code</th>
   </tr>
   <tr>
   <td><input datafld="addressee" ></input></td>
   <td><input datafld="streetaddress"></input></td>
        <td><input datafld="city"></input></td>
        <td><input datafld="postalCode"></input></td>
        </tr>
        </table>
        <input type="submit" value="Change"/>
        </form>
  </body>
 </Html>
</vIEw>
<model>
 <addressbook>
  <address>
        <addressee>John Smith</addressee>
        <streetaddress>250 18th Ave SE</streetaddress>
        <city>Rochester</city>
        <state>MN</state>
        <postalCode>55902</postalCode>
  </address>
  <address>
        <addressee>Bill Morris</addressee>
        <streetaddress>1234 Center Lane NW</streetaddress>
        <city>St. Paul</city>
        <state>MN</state>
  <postalCode>55123</postalCode>
  </address>
 </addressbook>
</model>
</combinedelement>


 

早先,我提到了將兩個輸入文檔合並成一個文檔。一個 XML 文檔只能有一個根元素,這就是我將 <combinedelement> 元素定義為新根元素的原因。按照 MVC 術語,我將為表示數據添加 <vIEw> 元素,為業務數據添加 <model> 元素。

  在下列代碼中:

<table id="AddressesPanel" dataSrc="addressbook" rowelement="address">

  dataSrc 屬性確定 <model> 元素中的元素,該元素必須顯示在表中。 rowelement 屬性定義哪個元素充當迭代符。

  表中的每個輸入元素都有一個 datafld 屬性,如下所示,它表明顯示了哪個業務數據元素的值。

<td><input datafld="addressee" ></input></td>
<td><input datafld="streetaddress"></input></td>
<td><input datafld="city"></input></td>
<td><input datafld="postalCode"></input></td>

  您可以用象 WebSphere Studio(請參閱 參考資料)之類的工具或允許創建 HTML 頁面的任何其它環境,方便地創建這裡顯示的表示數據。附加屬性必須以手工方式插入 Html 源文檔。然而,仍可以在工具中直觀地創建基本表示視圖。完成這一任務無需編程或 XSL 技能。當然,也可以根本不用任何工具的幫助來創建表示數據,如果您感到這樣最舒適的話。

  數據綁定樣式表

  最後一個難題是創建最終輸出格式的 XSLT 樣式表。可以獨立於實際應用程序開發該樣式表,因為它不包含任何特定於應用程序的信息。清單 5 中顯示的樣式表是不完整的,僅涵蓋 HTML 表的處理。然而,其它 Html 元素都可以以非常相似的方法來處理。

 

清單 5. 使用 XSLT 樣式表來處理 Html 表

<?XML version='1.0'?>
<xsl:stylesheet XMLns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">
 <xsl:output method="Html" indent="yes"/>
<!-- copy all elements which don't match any template -->
<xsl:template match="* | @*">
 <xsl:copy><xsl:copy-of select="@*"/><xsl:apply-templates/></xsl:copy>
</xsl:template>
<!-- delete the model element and all its content -->
<xsl:template match="model"/>
<!-- delete the combinedelement element, but not its content -->
<xsl:template match="combinedelement">
 <xsl:apply-templates/>
</xsl:template>
<!-- delete the vIEw element, but not its content -->
<xsl:template match="vIEw">
 <xsl:apply-templates/>
</xsl:template>
<!-- handle all <tr> elements -->
<xsl:template match="TR | tr">
 <xsl:choose>
  <!-- no special handling of the table header -->
  <xsl:when test="th">
  <xsl:apply-templates/>
  </xsl:when>
 <xsl:otherwise>
  <!-- store the name of the rowelement in a variable -->
  <!-- in the addressbook sample, this is 'address' -->
  <xsl:variable name="rowname">
  <xsl:value-of
    select='(ancestor::node()[name() = 'table'])/@rowelement"/>
  </xsl:variable>
  <!-- call the 'processRow' template, passing the rowelement -->
  <!-- and a counter -->
  <xsl:call-template name="processRow">
   <xsl:with-param name="numAddresses">
    <xsl:value-of select="count(//*[name() = $rowname])"/>
   </xsl:with-param>
   <xsl:with-param name="n">1</xsl:with-param>
  </xsl:call-template>
 </xsl:otherwise>
 </xsl:choose>
</xsl:template>
<!-- the processRow template calls itself recursively -->
<!-- for all rowelements -->
<xsl:template name="processRow">
 <xsl:param name="numAddresses"/>
 <xsl:param name="n"/>
 <!-- done iterating? -->
 <xsl:if test="$numAddresses >= $n">
  <xsl:copy>
   <!-- for all td's -->
   <xsl:for-each select="td">
    <!-- copy the element content -->
    <xsl:copy>
     <xsl:copy-of select="@*"/>
     <!-- find all elements with a 'datafld' attribute -->
     <!-- and save them in a variable for later use -->
     <xsl:for-each select="./*[@datafld]">
      <xsl:variable name="elname">
       <xsl:value-of select="./@datafld"/>
      </xsl:variable>
      <!-- copy the element, change the 'datafld' -->
      <!-- attribute into a 'name' attribute -->
      <xsl:copy>
       <xsl:copy-of select='(@*)[name() != 'datafld']"/>
        <xsl:attribute name="name">
         <xsl:value-of select="@datafld"/>
        </xsl:attribute>
        <!-- retrIEve the value of the element -->
        <!-- from the model by using the   -->
        <!-- 'datafld' attribute as a search -->
        <!-- argument -->
        <xsl:attribute name="value">
         <xsl:value-of
           select='(//*[name() = $elname])[position() = $n]"/>
        </xsl:attribute>
      </xsl:copy>
     </xsl:for-each>
    </xsl:copy>
   </xsl:for-each>
   <!-- call the template recursively, increase counter -->
   <xsl:call-template name="processRow">
    <xsl:with-param name="n">
     <xsl:value-of select="$n + 1"/>
    </xsl:with-param>
    <xsl:with-param name="numAddresses">
     <xsl:value-of select="$numAddresses"/>
    </xsl:with-param>
   </xsl:call-template>
  </xsl:copy>
 </xsl:if>
</xsl:template>
</xsl:stylesheet>

  清單 5 樣式表中有一個細節值得深入研究,因為它在許多情況下都有用。XSLT 沒有為處理循環定義任何機制,當必須多次處理同一個輸入元素以產生正確輸出時需要這些循環。可以通過定義一個遞歸調用其本身的模板來克服這個缺點。該模板將變量傳遞給其本身以充當計數器,從而定義循環何時結束。

  結束語

  在本文介紹的解決方案中,應用程序業務數據和表示數據被分開來開發,並由通用 XSLT 樣式表將它們合在一起,該解決方案並不完整;它只描述了概念。我期望以後將開發相似的(但完整的和健壯的)解決方案。

  然後,就可以使用象 清單 5 中所示的通用樣式表,而且在適用的地方,可以重用它。例如,結構化信息標准促進組織(Organization for the Advancement of Structured Information Standards (OASIS))提出了交互式應用程序 Web 服務(Web Services for Interactive Applications)倡議(請參閱 參考資料)。該倡議也基於 MVC 模式,它嘗試為可視的 Web 服務定義組件模型,這種模型將產生類似與在這裡表述的技術。


 

 

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