麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

使用SAX和XSLT實現復雜數據轉換(組圖)

2019-11-18 12:46:57
字體:
來源:轉載
供稿:網友


  摘要
  
  數據轉換總是難于實現的.XSLT (Extensible Stylesheet Language Transformations) 規范一個極好xml轉換工具,但是它僅能處理輸入數據是XML數據的情況, 而對于復雜轉換經常是困難的,甚至是不可能的. 本文闡述了在將XML數據提供給XSLT轉換器之前如何使用SAX對其進行”預處理 “. 預處理器能夠處理大多數復雜的任務,從而為XSLT器減少了不少負擔.本文附有幾個功能齊全的例子代碼 (可以從此處下載). 例子中使用的實施框架很容應用到許多不同的轉換解決方案中. (5,000 字; 2005年9月5日)
  
  在一個需要將原始的帳單數據轉換為不同的帳單布局展現的項目中, 我曾經被要求做簡單的數據轉換.看了問題的簡介后,我建議使用XSLT (Extensible Stylesheet Language Transformations).
  
  我進一步研究需求后發現問題沒有我預想的那么簡單.雖然輸入數據是易于治理的, 需要做簡單轉換的數據不能用一組靜態的XSLT樣式表單描述.一部分需要轉換的數據是動態的,而且存儲在兩個分離的數據庫中. 此外,為了呈現帳單的布局程序不得不對從兩個數據庫中取出的輸入數據做相對復雜的計算. XSLT解決方案被淡忘了.
  
  這個例子的核心問題是動態數據需要直接轉換. 在理想情況下,我們從來不會面對這個問題. 輸入數據的預備和 輸入數珠的轉換應該是清楚的分離開來的,從而所有需要轉換的數據能夠包含在一個單獨的XSLT模板中. 不幸的是,現實項目的需求有時候是相當的奇異.
  
  本文給出了上面描述的問題的一個解決方案.我將通過例子來展示如何利用 SAX(Simple API for XML) 來提高XSLT的適用性. 另外我將展示在輸入數據和期望的輸出數據都不是XML時如何使用XSLT.
  
  XSLT簡介
  
  XSLT 是一種轉換XML數據的編程語言. XSLT 表單樣式可以用來將XML文檔另外一種XML格式的文檔, 實際上可以轉換成任意其它格式. 但是XSLT可能不是一種簡單易學的語言, 尤其對于那些對類似java語言比較熟悉的人來說,它以一種強大而靈活的方式來完成相對復雜的數據轉換.假如你對XSLT還不是很熟悉, 有很多優秀的教程可用,例如, the XML Bible 的第17章.
  
  盡管XSLT是一種好的語言,但是用它來完成有些任務是困難的,甚至是不可能的. 對于必須對輸入的XML文檔的幾個元素做合并計算的這種轉換通常是可能行的, 但是通常極為難寫. 假如需要轉換的直接數據本身是動態的,那么單獨使用XSLT就不夠了. XSLT模板本質上是靜態的.盡管可以動態的重新產生模板,我仍然不認為這是一個可行的解決方案.(如有不同觀點請不吝賜教.)
  
  試驗了各種想法之后,我得出結論,用XSLT來完成復雜數據轉換的最簡單的辦法是在將輸入數據提交給XSLT轉換器處理之前對輸入數據進行巧妙的預處理. 這聽起來很令人費解,而且效率不高, 但是結果證實使用SAX處理輸入的XML數據是相當的簡單輕易.
  
  SAX 是一種事件驅動的用來解析XML文檔的接口. 當SAX解析器處理XML數據時,它產生解析器可以識別的XML元素的”回調(callback)”通知.例如,當解析器碰到XML開始標簽時,它產生一個回調事件startElement.標簽的名字和其它相關信息作為回調參數發送出去.當需要高效地界些XML時,應該使用SAX. 關于更多的SAX信息請看Sun's tutorial on JAXP. 本文中,在將數據提交給XSLT轉換器之前我使用SAX來修改事件流.
  
  SAX 和XSLT兩者都包含在JAXP (Java API for XML PRocessing) API中, J2SE 1.4版以后的版本中已經包含這個API.
  
  例子概要
  
  這部分介紹文章中的例子和SAX與XSLT結合的可能性.
  
  執行這些例子
  
  假如你對執行這些例子不感愛好,跳過這一部分. 但是本文嚴重依靠這些例子代碼,因此,建議您最好看看這些代碼.這些例子已經在Windows環境的J2SE 1.4.2 下測試過. 執行這些應用不需要引入其它API包. 但是本文假設您使用Ant 編譯工具.假如您不愿意使用Ant, 那么你仍然可以編譯并執行這些例子, 但是要額外多做一點點工作.
  
  具體的描述放在README.txt文件中, 它可以從下載的szip文件中取得. 您只要解開這個壓縮包并設置好相關變量(在README.txt中有說明),就可以使用下邊的Ant 命令了:
  
  ·ant build:刪除編譯過程產生的文件,重新編譯所有代碼
  
  ·ant clean:刪除編譯過程產生的文件
  
  ·ant example1: 執行例子1
  
  ·ant example2: 執行例子2
  
  ·ant example2b: 執行例子2b (例子2的一個變種)
  
  ·ant example3: 執行例子3
  
  ·ant example4: 執行例子4
  
  例子1概要
  
  盡管例子1是一個基本的XSLT轉換器, 但是介紹這個例子是有必要的, 因為他它是后續例子被編譯的基礎. 圖 1 展現了例子1的概念視圖.
  
 使用SAX和XSLT實現復雜數據轉換(組圖)(圖一)
點擊查看大圖

  圖 1. 例子一個的概念視圖.點擊看全圖.

  
  輸入數據(1.1) 為私有XML數據, 它模擬了客戶訂單報告. 數據大致如下:
  
  <?xml version="1.0"?><ORDER_INFO>  <CUSTOMER GROUP="exclusive">   <ID>234</ID>     <SERVICE_ORDERS>      <ORDER>        <PRODUCT_ID>1231</PRODUCT_ID>          <PRICE>100</PRICE>        <TIMESTAMP>2004-06-05:14:40:05</TIMESTAMP>      </ORDER>      <ORDER>        <PRODUCT_ID>2001</PRODUCT_ID>          <PRICE>20</PRICE>        <TIMESTAMP>2004-06-12:15:00:44</TIMESTAMP>      </ORDER>     </SERVICE_ORDERS>  </CUSTOMER>...
  
  例子 1的完整輸入數據在<EXAMPLE_ROOT>/input/orderInfo_1.1.xml文件中. 從此, <EXAMPLE_ROOT> 指代您解壓本文例子的那個目錄.
  
  圖1的XSLT 模板 (1.2)是一個將輸入數據轉換為Html格式的常規XSLT 樣式表單. XSLT模板大致如下:
  
  <?xml version="1.0"?>
  
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  
  <xsl:output omit-xml-declaration="yes"/>
  
  <xsl:template match="/">
  
  <xsl:apply-templates select="ORDER_INFO"/>
  
  </xsl:template>
  
  <xsl:template match="ORDER_INFO">
  
  <HTML>
  
  <HEAD>
  
  <TITLE>Customers' Order information</TITLE>
  
  </HEAD>
  
  <BODY>
  
  <H1>Customers' Order information</H1>
  
  <xsl:apply-templates select="CUSTOMER"/>
  
  <xsl:apply-templates select="PRICE_SUMMARY"/>
  
  </BODY>
  
  </HTML>
  
  </xsl:template>...
  
  完整的模板在<EXAMPLE_ROOT>/template/transform_1.2.xml文件中. 當您執行例子1時, 程序將輸出文件寫到<EXAMPLE_ROOT>/output/result_1.3.html文件中.
  
  為了讓讀者熟悉XSLT, 例子1應該是一個很簡單的好的編程練習.這個XSLT 模板僅僅包含格式化輸出的必要數據,而不包含任何復雜計算. 我們繼續看更有挑戰的例子2.
  
  例子2概要
  
  例子2可能是本文最有趣的一個例子.這個例子實際上由兩個不同的轉換組成(轉換2a和轉換2b), 我們分別考慮這兩部分,首先我們來看轉換2a. 例子2的概念視圖視圖如圖2所示.
  
<a href="/uploadImages/2007-5-2/2007529494597871.gif" target="_blank"> 使用SAX和XSLT實現復雜數據轉換(組圖)(圖二)</a>
  點擊看大圖
  圖 2. 例子2的概念視圖. 點擊看全圖.

  
  輸入數據 (1.1) 和 XSLT模板(1.2) 與例子1的相應部分對應.在XSLT轉換被應用之前,輸入數據通過由一組JAVA類組成的預處理器,預處理器通過SAX的時間過濾特性來操作XML數據. 數據源(datasource)是一組實現了一種虛數據源的JAVA類. 在真實的應用程序中, 數據源可能是數據庫接口. 引入這個虛數據庫是為了以一種極為簡單模式來展示從外部數據源取回的動態XML數據. 我希望讓這例子盡可能的輕易安裝和執行, 因此沒有實現任何真實的數據庫連接――希望虛數據源實現可以給你這個概念.
  
  在執行例子時,程序(假如mode參數設置為debug)回顯來自預處理器的XML數據給標準輸出流(System.out). 這些數據類似預處理器的輸出數據, 現在它是轉換器的輸入數據.回顯預處理器的輸出數據是一種方便的調試預處理轉換過程的方式. 本文稍后討論這個特性的實現.
  
  在執行轉換2a時, 下面數據被回顯到屏幕:
  
  <ORDER_INFO>
  
  <CUSTOMER GROUP="exclusive">
  
  <ID>     Jill   </ID>
  
  <SERVICE_ORDERS>
  
  <ORDER>
  
  <PRODUCT_ID>
  
  Doohickey
  
  </PRODUCT_ID>
  
  <PRICE>        100      </PRICE>
  
  </ORDER>...
  
  假如您把這個數據和原始輸入數據(<EXAMPLE_ROOT>/input/orderInfo_1.1.xml)比較一下, 將會注重到在數據的開始部分有如下不同:
  
  ·第一個CUSTOMER/ID元素的值從234變為Jill.
  
  ·第一個 CUSTOMER/SERVICE_ORDERS/ORDER/PRODUCT_ID元素的值從1231變為Doohickey.
  
  ·TIMESTAMP元素被去除掉了.
  
  預處理器已經用從內部模擬數據庫來的數據替換了CUSTOMER/ID和 CUSTOMER/SERVICE_ORDERS/ORDER/PRODUCT_ID元素的值. 同時它過濾掉了TIMESTAMP元素和它的值. 這些修改后的數據現在提供給XSLT轉換器作為輸入.
  
  轉換 2a的輸出文件是<EXAMPLE_ROOT>/output/result_2.1.html.
  
  在您執行轉化2b時, 咋一看回顯的屏幕的數據與轉換2a的回顯數據很相似. 不同在于預處理器在最后部分插入了PRICE_SUMMARY元素:
  
  <PRICE_SUMMARY>
  
  <PRODUCT>
  
  <NAME>
  
  Doohickey
  
  </NAME>
  
  <SUM>      110     </SUM>
  
  </PRODUCT>
  
  <PRODUCT>
  
  <NAME>      Nose Cleaner     </NAME>
  
  <SUM>      10     </SUM>
  
  </PRODUCT>
  
  <PRODUCT>
  
  <NAME>
  
  Raccoon     </NAME>
  
  <SUM>      40     </SUM>
  
  </PRODUCT>
  
  </PRICE_SUMMARY>
  
  </ORDER_INFO>
  
  這個例子的目的是用來證實預處理器也可以用來引入新的XML元素, 元素的值可以直接來自輸入的XML數據的計算, 也可以使用一些來自于外部數據源的補充數據.
  
  例子2b 的輸出文件是 <EXAMPLE_ROOT>/output/result_2.1b.html.
  
  這些例子為什么令人感愛好?在概念層面上,這個數據轉換方法似乎并沒有什么創新. 令人感愛好的是使用SAX對輸入的XML數據做微小的動態的改善是相對輕易實現的, 而這可是使本來使用純XSLT無法實現的轉換成為可能. 另一方面, 僅僅使用SAX來實現整個轉換是有可能的,但是有點單調乏味. 在對復雜數據的實施轉換時使用SAX和XSLT相結合幾乎無所不能.
  
  在"深入研究例子2"部分,我會討論如何擴展帶有預處理器的XSLT轉換器的模式. 你將會看到,只要稍微修改一下本文的例子代碼,它就可以有很多不同的用途.
  
  例子3和例子4提升了例子2對非XML數據的讀寫能力. 假如你對這些不感愛好,可以直接例子1和例子2的實施細節部分.
  
  例子3概要
  
  有時候, 輸入數據來自幾個不同的數據源;有時候,這些數據并非都時XML格式的數據.例子3說明了對于非XML數據如何使用SAX產生事件, 從而使應用XSLT成為可能. 例子3的概念視圖如圖3.
  
<a href="/uploadImages/2007-5-2/2007529494642024.gif" target="_blank"> 使用SAX和XSLT實現復雜數據轉換(組圖)(圖三)</a>
  點擊看大圖
  圖 3. 例子3的概念視圖. 點擊看全圖.

  
  例子3的輸入數據大致如下:
  
  3exclusive:2342Order:1231Price:100Timestamp:2004-06-05:14:40:05Order:2001Price:20Timestamp:2004-06-12:15:00:44...
  
  例子3的完整輸入文件是<EXAMPLE_ROOT>/input/orderInfoAsText_3.1.txt.
  
  XML產生器讀取這些數據并產生SAX事件,用以匹配前面例子(例子1和例子2)的XML輸入文檔. 從而預處理器接收到的數據與例子2的預處理器接收到的輸入數據是相似的.轉換的其余部分與轉換2b類似. 結果輸出文件(<EXAMPLE_ROOT>/input/result_3.2.html) is also similar to 與轉換2b的輸出文件 (file <EXAMPLE_ROOT>/output/result_2.1b.html)也是相似的.
  
  例子4概要
  
  XML并非總是我們希望得到的輸出格式. 此例類似前例,但是它產生的是一個純文本文件的輸出而不是XML文件T. 見圖4,它是個概念視圖.
  
<a href="/uploadImages/2007-5-2/2007529494698811.gif" target="_blank"> 使用SAX和XSLT實現復雜數據轉換(組圖)(圖四)</a>
  點擊看大圖
  圖 4. 例子4的概念視圖4. 點擊可見完整尺寸的圖片

  
  輸入數據跟例子3相同(<EXAMPLE_ROOT>/input/orderInfoAsText_3.1.txt), 而XSLT 模板不同. 既然讀者現在已經熟悉XSLT, XSLT轉換也可以用來產生非XML數據. XSLT 模板 (4.1) 是個用來將輸入數據轉換為文本根是的規則的XSLT 樣式表單, 模板大致如下:
  
  <?xml version="1.0"?>
  
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  
  <xsl:output method="text" omit-xml-declaration="yes"/>
  
  <xsl:template match="/">
  
  <xsl:apply-templates select="ORDER_INFO"/>
  
  </xsl:template>
  
  <xsl:template match="ORDER_INFO">Customers' Order information
  
  <xsl:apply-templates select="CUSTOMER"/>
  
  <xsl:apply-templates select="PRICE_SUMMARY"/>
  
  </xsl:template>
  
  <xsl:template match="CUSTOMER">Customer id:<xsl:value-of select="ID"/>
  
  Customer group is '<xsl:value-of select="@GROUP"/>'
  
  <xsl:apply-templates select="SERVICE_ORDERS"/>
  
  </xsl:template>...
  
  完整的轉換模板<EXAMPLE_ROOT>/template/transform_4.1.xml文件中. 當運行例子4的時候,程序將輸出寫在<EXAMPLE_ROOT>/output/result_4.2.txt中
  
  除了使用XSLT模板以外,例子4的代碼和例子3的代碼是一樣的.
  
  代碼查看
  
  讓我們看看代碼的實現
  
  Example 1
  
  Example 1的Transformer實體由class Example1 ,ExampleTester和Example接口實現。Class ExampleTester解析輸入參數,創建Example1的實例,Example1實現了Example接口,并且調用doTransform()方法。圖例5是Example的類圖。綠色框內的是J2SE的標準庫。如StreamSource,StreamResult和javax.xml.transform.stream
  
 使用SAX和XSLT實現復雜數據轉換(組圖)(圖五)
  Figure 5. Class diagram of Example 1. Click on thumbnail to view full-sized image.

  
  Example1 的doTransform() 方法 in Example1 比較有趣. 讓我們仔細閱讀:
  
  1. public doTransform(String _inputFileName,      String _transformerFileName,      String _outputFileName) {2.   try {3.    initTransformer(_transformerFileName);4.   } catch (TransformerConfigurationException tce) {5.     // omitted for clarity6.   } 7.   Source myXMLSource = getInputSourceObject(_inputFileName);8.   Result myResult = getResultObject(_outputFileName)9.   try {   10.    myTransformer.transform(myXMLSource, myResult); 11.  } catch (TransformerException te) {12.    // omitted for clarity 13.  }14. }
  
  doTransform() 方法有3個參數:
  
  ·_inputFileName: 讀入xml數據的源文件名
  
  ·_transformerFileName: XSLT 文件名
  
  ·_outputFileName: 轉換XML數據后輸出的文件名
  
  在第3行, 調用initTransformer() 方法來創建XSLT 轉換器(transformer). 類 myXMLSource 是需要被轉換的數據源. 在第7行, 調用getInputSource() 來創建myXMLSource 實例. 源數據轉換后寫入myResult 對象,在第8行, getResultObject() 方法根據傳入的參數_outputFileName 來創建輸出文件. 做好這些預備步驟后,轉換工作在第10行完成:
  
  myTransformer.transform(myXMLSource, myResult);
  
  開始進入第2個例子前,考慮一下getInputSourceObject()方法創建的source實例:
  
  Source myXMLSource = getInputSourceObject(_inputFileName);
  
  這是 XSLT 轉換器的數據源. 3個類實現了Source接口: StreamSource, SAXSource, and DOMSource. 這篇文章只用了 StreamSource 和 SAXSource (see Figure 6).
  
<a href="/uploadImages/2007-5-2/2007529494789829.gif" target="_blank"> 使用SAX和XSLT實現復雜數據轉換(組圖)(圖六)</a>
  點擊看大圖
  Figure 6. XSLT sources. Click on thumbnail to view full-sized image.
  

  Example 1中, getInputSourceObject() 方法返回一個 StreamSource對象. 數據來自文件 (<EXAMPLE_ROOT>/input/orderInfo_1.1.xml) 。
  
  下個例子的XML數據依然來自文件,不同的是使用了SAX事件流處理。 實際上,Example 1的轉換器接受事件流比接受文件流不會有多大麻煩。 然而在下一個例子中,整個思想基于捕捉,操控SAX事件流的能力
  
  A deeper look into Example 2
  
  Example 2's 預處理的實現包含 (in Transformation 2a) 類 Example2 和包 myutil.里的類
  
  類 Example2 繼續了Example1. 該類重載了 getInputSourceObject()方法,聲明的命令工廠的名字. 先仔細研究getInputSourceObject():
  
  1. protected Source getInputSourceObject(String _pathName) throws FileNotFoundException {
  
  2. InputSource inputSource = getInputSourceFromFile(_pathName);
  
  3. XMLReader xmlFilter = getFilteringReader();
  
  4. SAXSource saxSource = new SAXSource(xmlFilter, inputSource);
  
  5. return saxSource;
  
  6. }
  
  第2行, getInputSourceFromFile() 方法從文件中創建InputSource 實例 ,第4行, SAXSource 實例被InputSource 和 XMLReader創建?!MLReader 實例的創建值得關注,讓我們進入getFilteringReader 方法看看:
  
  1. private XMLReader getFilteringReader() {
  
  2.   XMLReader myReader = getReader();
  
  3.   XMLFilterImpl xmlFilter = new ModifyingXMLSource(myReader, FACTORY_NAME);
  
  4.   if ((MODE != null) && (MODE.equals("debug"))) {
  
  5.    xmlFilter = new XMLPrinter(xmlFilter);
  
  6.   }
  
  7.   return xmlFilter;8. }
  
  第2行, XMLReader 被創建. 這是缺省的reader,它解析原始輸入數據并發送SAX事件到內容處理器―― 內容處理器在這里就是XSLT轉換器。第3行創建了 ModifyingXMLSource實例, 它是一個SAX事件過濾器的實現,是這篇文檔的核心.第 5行,第2個事件過濾器,XMLPrinter被創建。 Figure 7 顯示結果對象的結構。
  
 使用SAX和XSLT實現復雜數據轉換(組圖)(圖七)
點擊查看大圖

  Figure 7. Object diagram of Example 2. Click on thumbnail to view full-sized image.
  

  SAXSource現在運行后,SAX事件先被ModifyingXMLSource 對象處理, 而后決定是否下一個事件過濾器XMLPrinter處理. 你會看到, ModifyingXMLSource 也可生成并轉發新事件。XMLPrinter 轉發所有事件到最終目標XSLT transformer. Figure 8 顯示了這個class的結構。
  
<a href="/uploadImages/2007-5-2/2007529494878685.gif" target="_blank"> 使用SAX和XSLT實現復雜數據轉換(組圖)(圖八)</a>
  點擊看大圖
  Figure 8. Class diagram of Example 2. Click on thumbnail to view full-sized image.
  

  為了明白fillter的功能,讓我們看看XMLPrinter 的代碼, XMLPrinter打印XML 數據到標準輸出流并且轉發SAX事件到他的父類。他的結構如下:
  
  public class XMLPrinter extends XMLFilterImpl {
  
  private CharArrayWriter contents = new CharArrayWriter();
  
  private String indent = "";
  
  public XMLPrinter(XMLReader _reader)
  
  {   super(_reader);    };
  
  public void startElement(String _uri,
  
  String _localName,
  
  String _qName,
  
  Attributes _atts) throws SAXException
  
  {   // Print the indentation    // Print start tag    // Increase the indent
  
  super.startElement(_uri, _localName, _qName, _atts);
  
  }  public void characters(char[] _ch,
  
  int _start,
  
  int _length) throws SAXException {
  
  contents.write(_ch, _start, _length);
  
  super.characters(_ch, _start, _length);  };
  
  public void endElement(String _uri,
  
  String _localName,
  
  String _qName) throws SAXException {
  
  XMLPrinter 繼續了 XMLFilterImpl, XMLFilterImpl 提供了全部SAX事件回調方法的默認實現。 假如你不重載任何方法,事件流通過這個過濾器不會有任何改變。 XMLPrinter 包含方法 startElement(), characters(), 和 endElement(), 這些是打印XML文檔基本內容到標準輸出流的必需的方法。 當碰到XML元素的標簽時, startElement()總是被調用(例如, <TIMESTAMP>). 碰到XML元素的內容時,characters() 方法被調用 (對如 TIMESTAMP 元素, 內容就是 2004-06-05:14:40:05). 碰到XML元素的結束標簽時, endElement() 被調用。
  
  例如,在XMLPrinter, 可打印的XML開始標簽被構建和打印在startElement()方法內,注重在每個方法的結束時,父類的相應方法總是被調用。 因此,整個事件流保持不變。
  
  對于ModifyingXMLSource, 象 XMLPrinter一樣, 重載 了startElement(), characters(), and endElement()方法. 這個類使用回調方法startElement(), characters(), or endElement()處理接受到的SAX事件。它查找Command 對象來處理事件,大部分的邏輯處理在分開的command類里實現,這樣可以保持 ModifyingXMLSource 小而簡單。
  
  讓我們看看Commands 怎樣和XML的元素映射. command-to-element 的映射在工廠類里完成。 創建 ModifyingXMLSource 時,初始化了一個 command 工廠. 在Example 2 (Transformation 2a), 這個工廠時Example2CommandFactory 類。
  
  1. public Example2CommandFactory() {
  
  2.   CustomerIdCommand myCustomerIdCommand = new CustomerIdCommand();
  
  3.   commands.put("/ORDER_INFO/CUSTOMER/ID", myCustomerIdCommand);
  
  4.   FilterCommand myFilterCommand = new FilterCommand();
  
  5.   commands.put(     "/ORDER_INFO/CUSTOMER/SERVICE_ORDERS/ORDER/TIMESTAMP",     myFilterCommand);
  
  6.   ProductIdCommand myProductIdCommand = new ProductIdCommand();
  
  7.   commands.put(     "/ORDER_INFO/CUSTOMER/SERVICE_ORDERS/ORDER/PRODUCT_ID",     myProductIdCommand);
  
  8. }
  
  第3行, 元素Customer的子元素ID 映射到 CustomerIdCommand 處理,第5行 TIMESTAMP 元素映射到 FilterCommand, 第7行 PRODUCT_ID元素映射到 ProductIdCommand. 全部 command 類實現了Command 接口:
  
  1. public interface Command {
  
  2.   public void reset();
  
  3.   public Object getResult();
  
  4.   public void startElement(String _uri,
  
  String _localName,
  
  String _qName,
  
  Attributes _atts,
  
  XMLFilterImpl _caller,
  
  DefaultHandlerInterface _default) throws SAXException;
  
  5.   public void characters(char[] ch,
  
  int start,
  
  int length,
  
  XMLFilterImpl _caller,
  
  DefaultHandlerInterface _default) throws SAXException;
  
  6.   public void endElement(String _uri,
  
  String _localName,
  
  String _qName,
  
  XMLFilterImpl _caller,
  
  DefaultHandlerInterface _default) throws SAXException;
  
  7. }
  
  Commands 可以用來收集和合并數據,所以重置command的狀態顯然是需要的。 The getResult() 可以 method被其他command調用,用于交換信息 。有了startElement(), characters(), and endElement() (Lines 4, 5, and 6), ModifyingXMLSource 可以很輕易委托 SAX 事件給Command對象處理。
  
  ModifyingXMLSource 通過工廠獲得commands, 如他的 startElement()方法所示:
  
  1. public void startElement(String _uri,
  
  String _localName,
  
  String _qName,
  
  Attributes _atts) throws SAXException {
  
  2.   tagIdentifier += ("/" + _localName);
  
  3.   try {
  
  4.    currentCommand = factory.getCommand(tagIdentifier);
  
  5.    currentCommand.startElement(_uri, _localName, _qName, _atts,     this, this);
  
  6.   } catch (SAXException sax) {...
  
  characters() 和 endElement() 方法 遵循同一個原理, tagIdentifier() 跟蹤XML事件流中元素,用于獲取正確的Command。
  
  讓我們首先看看 FilterCommand 類, 當 tagIdentifier 值等于/ORDER_INFO/CUSTOMER/SERVICE_ORDERS/ORDER/TIMESTAMP, 工廠類factory 返回 FilterCommand對象. FilterCommand's startElement(), characters(), 和 endElement() 方法什么也沒處理,NullCommand一樣,<TIMESTAMP>元素隨他的內容一起原樣慮過
  
  開始查看CustomerIdCommand 和 ProductIdCommand前, 看看Figure 9里的類的結構 (注重圖中CustomerIdCommand 省略了).
  
<a href="/uploadImages/2007-5-2/2007529494885166.gif" target="_blank"> 使用SAX和XSLT實現復雜數據轉換(組圖)(圖九)</a>
  點擊看大圖
  Figure 9. Class diagram of Example
  

  2, continued. Click on thumbnail to view full-sized image.
  
  CustomerIdCommand 和 ProductIdCommand commands 從datasource (參看 Figure 2)中接受數據.下列代碼演示了怎樣把ID映射成名字字符串 :
  
  1. public void characters(char[] ch,     int start,     int length,     XMLFilterImpl _caller,     DefaultHandlerInterface _default) throws SAXException {
  
  2.   contents.write(ch, start, length);
  
  3. };
  
  4. public void endElement(String _uri,
  
  String _localName,
  
  String _qName,
  
  XMLFilterImpl _caller,
  
  DefaultHandlerInterface _default) throws SAXException {
  
  5.   idString = contents.toString();
  
  6.   DataaccessorFactory myFactory =
  
  DataAccessorFactory.getFactory();
  
  7.   DataAccessor myAccessor = myFactory.getDataAccessor();
  
  8.   idString = myAccessor.getProductName(idString);
  
  9.   _default.defaultCharactersHandler(idString.toCharArray(), 0,     idString.length());
  
  10.  _default.defaultEndElementHandler(_uri, _localName, _qName);11. };
  
  竅門在于characters() 方法沒有馬上調用 defaultCharactersHandler, 因此原始數據沒有被轉發(forWord)。 在endElement() 方法, defaultCharactersHandler() 和 defaultElementHandler() 被調用called. defaultCharactersHandler()調用時 is 傳如了來自DataAccessor的改變的值called with a modified value received from DataAccessor. DataAccessor 接口interface如下:
  
  1. package myutil.dataAccess;
  
  2. public interface DataAccessor {
  
  3.   public String getCustomerName(String _customerId);
  
  4.   public String getProductName(String _productId);
  
  5. }
  
  getProductName()根據給定的參數返回產品名,在這個例子中時參數是1231。
  
  CustomerIdCommand與 ProductIdCommand類似,不同點在于它調用的是getCustomerName()而不是getProductName()。
  
  Example 2 的第2部分是Transformation 2b, 它添加小結信息到XML數據中 (PRICE_SUMMARY 元素). Example2b 如下:
  
  1. public class Example2b extends Example2 {
  
  2.   public Example2b() {
  
  3.    super();
  
  4.    FACTORY_NAME = "Example2b";
  
  5.   }
  
  6. }
  
  正如你看到,他的美麗的設計,在于只要繼續Example2,賦給command工廠一個新的名字 。
  
  在 CommandFactory's getInstance()方法中,工廠名 Example2b 映射到Example2bCommandFactory 對象。 Example2bCommandFactory 與Example2CommandFactory 類似。不同點在于Example2bCommandFactory'的構造函數包含如下新行:
  
  1. PriceCollectorCommand myPriceCollectorCommand =    new PriceCollectorCommand(myProductIdCommand);
  
  2. commands.put("/ORDER_INFO/CUSTOMER/SERVICE_ORDERS/ORDER/PRICE",    myPriceCollectorCommand);
  
  3. commands.put("/ORDER_INFO",    new PriceSummaryPrintingCommand(myPriceCollectorCommand));
  
  PriceCollectorCommand從輸入XML數據中收集全部 PRICE 元素。 PriceSummaryPrintingCommand 要從PriceCollectorCommand收集Price信息并輸出成XML格式。所以,PriceSummaryPrintingCommand 引用PriceCollectorCommand對象在他的構造函數中 (第3行). 圖 10 為 Example 2b的類圖。
  
 使用SAX和XSLT實現復雜數據轉換(組圖)(圖十)
點擊查看大圖

  Figure 10. Class diagram of Example
  

  2, Transformation 2b. Click on thumbnail to view full-sized image.
  
  注重僅僅新的Command類顯示在圖10中?!嶋H上 Example2bCommandFactory與Example2CommandFactory一樣, 有相同的Command 依靠。
  
  讓我們深入查看一個 Command 類. 下面是PriceCollectorCommand 的部分代碼:
  
  1. public void endElement(String _uri,
  
  String _localName,
  
  String _qName,
  
  XMLFilterImpl _caller,
  
  DefaultHandlerInterface _default) throws SAXException {
  
  2.   String productIdAsKey = (String)myProductIdCommand.getResult();
  
  3.   String priceAsString = contents.toString();
  
  4.   Integer price = priceToNumber(priceAsString);
  
  5.   if (price != null) {
  
  6.    addPriceToHashMap(productIdAsKey, price);
  
  7.   }
  
  8.   _default.defaultEndElementHandler(_uri, _localName, _qName);
  
  9. };
  
  在endElement() 方法中,addPriceToHashMap() 方法存入了每種產品的總值,詳情請看PriceCollectorCommand.java的代碼。
  
  同樣,PriceSummaryPrintingCommand 的endElement() 方法的代碼是令人感愛好的:
  
  1. public void endElement(String _uri,
  
  String _localName,
  
  String _qName,
  
  XMLFilterImpl _caller,
  
  DefaultHandlerInterface _default) throws SAXException {
  
  2.   insertSummary(_default, _uri);
  
  3.   _default.defaultEndElementHandler(_uri, _localName, _qName);
  
  4. };
  
  在insertSummary() 方法中, PriceSummaryPrintingCommand 在默認的結束元素處理事件前插入他的XML 內容 。insertSummary() 從PriceCollectorCommand 的 getResult() 方法中接受價格匯總信息,然后根據hashmap的內容激發SAX事件。 Command 類使用了ModifyingXMLSource的默認方法 (defaultstartElementHandler(), defaultCharactersHandler(), 和 defaultEndElementHandler()) 來激發新的SAX事件.詳見 PriceSummaryPrintingCommand.java.
  
  深入查看Example3
  
  Example 3可以從非XML數據生成新的XML元素,這不是一個生疏的主意。他的竅門是有了我們自己的XML reader (見 Figure 11).
  
 使用SAX和XSLT實現復雜數據轉換(組圖)(圖十)
  Figure 11. Class diagram of Example 3
  

  類Example3很簡單:
  
  1. import org.xml.sax.XMLReader;
  
  2. import org.xml.sax.SAXException;
  
  3. import org.xml.sax.helpers.XMLReaderFactory;
  
  4. public class Example3 extends Example2 {
  
  5.   public Example3() {
  
  6.    super();
  
  7.    FACTORY_NAME = "Example2b";
  
  8.   }
  
  9.   protected XMLReader getReader() {
  
  10.    XMLReader myReader = new Example3Reader();
  
  11.    return myReader;
  
  12.  }
  
  13. }
  
  在這個類里,只有重載的 getReader() 方法是新的。 因為輸入數據不是XML數據,所以我們需要使用自己定制的reader, 它在第10行創建。
  
  Example3Reader 需要實現XMLReader接口的全部方法。然而,接口中的大部分方法可以虛擬實現(原文為dummy implementation, 理解為方法體內直接返回)。值得關注的是parse(), setContentHandler(), 和 getContentHandler(). Example3Reader包含 一個成員類 ContentHandler ,變量名為myHandler. setContentHandler() 是他的設置方法:
  
  1. public void setContentHandler(ContentHandler _handler) {
  
  2.   myHandler = _handler;
  
  3. }
  
  ContentHandler 提供了產生SAX事件回調方法。Transformer 在XSLT轉換開始前調用setContentHandler() 方法。在這個例子中,調用發生在 Example1的 doTransform() 方法, 更進一步說, 是 Transformer的 transform() 方法中。
  
  設置 ContentHandler后, Transformer 調用 Example3Reader的 parse() 方法. 這個方法實現了自有的解析器來解析文本格式數據。 解析的原理是XML文檔在ContentHandler 方法中構建。在我們的簡單的例子中, 只需要下列5個回調方法:
  
  ·startDocument():XML 文檔的開始
  
  ·startElement(String, String, String, Attributes): XML 元素開始
  
  ·endElement(String, String, String): XML 元素結束
  
  ·characters(char[], int, int): XML 元素的內容
  
  ·endDocument():XML 文檔結束
  
  如下面的調用與price元素對應:
  
  myHandler.startElement("", "<PRICE>", "<PRICE>", new AttributesImpl());// Converting String "20" to char array char[] myChArray = new char[255];"20".getChars(0, 2, myChArray, 0);// Conversion donemyHandler.characters(myChArray, 0, 2);myHandler.endElement("", "<PRICE>", "<PRICE>");
  
  對應的 XML 元素:
  
  <PRICE>20</PRICE>
  
  總結
  
  這篇文章主要介紹怎樣使用SAX和XSLT完成復雜的數據轉換,用SAX處理XML數據進行“預處理“,而后XSLT進行轉換. Example 1 介紹了基本的XSLT 轉換器. Example 2 演示了怎樣操控XML數據,并提供給XSLT轉換器。 Example 3 演示了怎樣從非XML數據生成XML數據,進而應用XSLT轉換處理,Example 4 演示了怎樣使用XSLT生成非XML數據。
  
  結合 SAX 和 XSLT 是完成復雜數據轉換工作的強有力的工具。 它還可以動態的改變轉換規則.另一方面,這中技術可能被濫用,因為即使在不應該使用它的情況下的轉換引擎中,它很輕易隱藏商業邏輯, 例如,很少推薦在整合層(integration layer)包含任何商業邏輯 。
  
  當不再使用XSLT模板包含全部的轉換邏輯,維護變得困難。 可以編寫command代碼,使轉換邏輯的數據來自屬性文件,因此避免每次轉換規則變了后需要重新編輯。盡管如此, 讓事情保持可控的最好方法還是有文檔說明,并隨時保持文檔更新。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 亚洲成人精品在线 | 精品国产一区二区三区四 | 成人aaaaa片毛片按摩 | 久久久久久久亚洲视频 | av国产片| 国产亚洲精品影达达兔 | 看毛片免费 | 国产美女视频一区 | 91小视频在线观看免费版高清 | 激情久久一区二区 | 国产精品久久久久久模特 | 成人毛片在线免费看 | 精品影视一区二区 | 国产精品久久久久一区二区 | 又黄又爽免费无遮挡在线观看 | 亚洲成人免费视频在线 | 午夜精品小视频 | 日韩字幕在线 | 色综合网在线观看 | 黄污网站在线观看 | 91久久久久久亚洲精品禁果 | 国产成人av免费观看 | 91毛片网站| 视频一区国产 | 看个毛片 | 成人18网站 | 久久久久久久久久久亚洲 | 牛牛视频在线 | 久久综合久久综合久久 | 九九热免费在线观看 | 欧美一级性 | 性爱视频在线免费 | 1区2区3区国产 | 久久亚洲精品视频 | 久草最新网址 | 全黄性性激高免费视频 | 久久精品无码一区二区三区 | 圆产精品久久久久久久久久久 | xxxxxx免费 | 国产成人强伦免费视频网站 | av免费在线播放网址 |