JAXP 再述??Sun 的 Java API for XML Parsing,1.1 版(轉)

amyz發表於2007-08-12
JAXP 再述??Sun 的 Java API for XML Parsing,1.1 版(轉)[@more@]

  繼上篇關於 JAXP(Sun 的 Java API for XML Parsing)的文章之後,在本續篇中,作者分析了對 SAX 和 DOM 標準支援進行了更新的最新版本 1.1。新增了 TRaX 之後,JAXP 1.1 為 Java 和 XML 開發人員提供了在編寫對 XML 文件進行語法分析和變換的獨立於供應商的程式碼方面不可缺少的工具。

  如果您經常閱讀 developerWorks 的 XML 專區,就可能對另一篇 JAXP 文章的出現感到有些奇怪。就在一個月前,我寫了一篇文章“ JAXP 專述 ”。在那篇文章中,我完整地解釋了 JAXP(Java API for XML Parsing),其工作原理,以及它如何幫助您用 Java 程式處理 XML 資料。那篇文章講的是 JAXP 發行版 1.0。

  熟悉的領域

  那麼,為什麼還要寫 JAXP 方面的文章呢?我是 JAXP 專家小組的成員之一,我們現在即將完成 1.1 規範。雖然大多數“點發行版”(指的是,例如,版本從 1.0 升到 1.1,或從 2.2 升到 2.3)只對現有的 API 作很小、或者至少是簡單的改動,但是 JAXP 的 1.1 發行版卻與其前一版有很大不同。事實上,本文只有三分之一講述現有類和功能性中的新方法,而其餘部分將集中講述 JAXP 1.1 版完全新的類和功能。換句話說,JAXP 1.1 中的新東西(也是好東西)實在太多,我迫不及待地要讓您感受一下它們。

  如果是 JAXP 新手,或者現在正在使用它,或者要等它再成熟一些時使用,那麼,本文正適合您。我將講述對 API 1.0 版所作的修改,然後再花一些時間講一下 TRaX(XML 變換)。TRaX 是合併到 JAXP 中來允許用獨立於供應商的方式進行 XSL 變換的 API,它補充了 JAXP 在進行 XML 語法分析時允許供應商獨立性的現有能力。休息片刻,然後再讀這篇 JAXP 1.1 的討論。

  增強語法分析 API

  很多對 JAXP API 的改動都圍繞語法分析進行,考慮到 JAXP 中的 "P" 代表 "parsing"(語法分析),這是有意義的。但是,JAXP 1.1 中的重大改動是圍繞 XML 變換進行的,以後將在本文中介紹。在現有的 JAXP 功能性方面,改動非常少。最大的增加是對 SAX 2.0(在 2000 年 5 月完成)的支援,和對 DOM 級別 2(還在完成工作中)級別 2 的支援。前一版的 JAXP 只支援 SAX 1.0 和 DOM 級別 1。這種更新標準的缺乏曾一度是 JAXP 1.0 中最受批評之處。

  除了使 JAXP 支援 SAX 和 DOM 的最新版之外,API 中還有幾處小的改動(如我上一篇文章所述)。幾乎所有這些改動都是透過不同公司和個人對 JAXP 專家小組提供的反饋而產生的重要改動。所有這些改動還處理由 JAXP 的兩個 factory( SAXParserFactory 和 DocumentBuilderFactory )返回的語法分析器的配置問題。現在,我將講述這些,以及 SAX 和 DOM 標準支援中的更新。

  更新標準

  從 JAXP 1.0 到 1.1 的升級中,最令人期待的改變是對流行的 SAX 和 DOM 標準支援的更新。SAX (Simple API for XML) 在 2000 年 5 月發行了 2.0 版,與 XML 其它元件相比,該版本對 XML 名稱空間的支援提供了極大增強。這種名稱空間的支援允許使用眾多其它 XML 詞彙,如 XML 模式、XLink 和 XPointer。雖然在 SAX 1.0 中也可以使用這些詞彙,但是開發人員需要將元素的本地(或限定)名稱與其名稱空間分開,並在整個文件中跟蹤這些名稱空間。SAX 2.0 為開發人員提供了這種資訊,從而極大簡化了執行這些程式設計任務的過程。DOM 級別 2 也是一樣:有名稱空間支援和許多 DOM 類中的其它方法。雖然 DOM 級別 2 還沒有完成,但是,JAXP 1.1 支援其目前的規範。如果 DOM 標準的最終版本引入了小的改動,JAXP 當然要包括這些修改。

  好的訊息是:這些改動對使用 JAXP 的開發人員來說通常都是透明的。換句話說,這些標準更新可以說是“自動”發生的,無需使用者介入。只要對 SAXParserFactory 指定與 SAX 2.0 相容的語法分析器,並對 DocumentBuilderFactory 類指定與 DOM 級別 2 相容的語法分析器,就可以利用這種更新。

  通往 SAX 2.0 之路

  有幾個與這些更新相關的重要改動。在 SAX 1.0 中,由供應商和 XML 語法分析器專案實現的語法分析器介面是 org.xml.sax.Parser 。然後,JAXP 類 SAXParser 透過 getParser() 方法提供一個方法來獲得這個底層實現類。該方法的特徵如下:

  清單 1. getParser() 方法

  public interface SAXParser {public org.xml.sax.Parser getParser();// Other methods}

  然而,在從 SAX 1.0 到 2.0 的改動中,反對使用 Parser 介面,並將其用新介面 org.xml.sax.XMLReader 替代。這在本質上使 getParser() 方法在獲得 SAX 2.0 XMLReader 類的例項方面已無用處。為支援這種做法並支援 SAX 2.0,將一個新方法新增到 JAXP SAXParser 類。自然地,將該方法命名為 getXMLReader() ,它看起來如下:

  清單 2. getXMLReader() 方法

  public interface SAXParser {public org.xml.sax.XMLReader getXMLReader();public org.xml.sax.Parser getParser();// Other methods}

  同樣,在 SAX 1.0 中用來實現回撥的類是 org.xml.sax.HandlerBase ,並將該類的例項提供給所有 JAXP 1.0 parse() 方法。但是,由於某些其它的 SAX 2.0 不支援和改動,在 SAX 2.0 中已不再使用這個類。取代它的是 org.xml.sax.ext.DefaultHandler 。為適應這種改動,為 SAXParser 類中的所有 parse() 方法補充了接受 DefaultHandler 類例項以支援 SAX 2.0 的同一方法的不同版本。為幫助您看到這一不同之處,清單 3 中顯示了所討論的方法:

  清單 3. parse() 方法

  public interface SAXParser {// The SAX 1.0 parse methodspublic void parse(File file, HandlerBase handlerBase);public void parse(InputSource inputSource, HandlerBase handlerBase);public void parse(InputStream inputStream, HandlerBase handlerBase);public void parse(InputStream inputStream, HandlerBase handlerBase,String systemID);public void parse(String uri, HandlerBase handlerBase);// The SAX 2.0 parse methodspublic void parse(File file, DefaultHandler defaultHandler);public void parse(InputSource inputSource, DefaultHandler defaultHandler);public void parse(InputStream inputStream, DefaultHandler defaultHandler);public void parse(InputStream inputStream, DefaultHandler defaultHandler,String systemID);public void parse(String uri, DefaultHandler defaultHandler);// Other methods}

  所有這些方法都用來進行語法分析可能有點令人困惑,但是,只有在使用 SAX 的 兩個 版本時才會令人感到棘手。如果正在使用 SAX 1.0,那麼將使用 Parser 介面和 HandlerBase 類,應該使用哪些方法也很明顯。同樣,在使用 SAX 2.0 時,很明顯,應該使用那些接收 DefaultHandler 例項並返回 XMLReader 的方法。因此,請把所有這些都看成是參考,不要對此太擔心。另外,還對 API 的 SAX 部分作了某些其它改動。

  現有 SAX 類中的改動

  要完成對現有 JAXP 功能性改動的討論,需要複習幾個 JAXP SAX 使用者可以使用的新方法。首先, SAXParserFactory 類有一個新方法: setFeature() 。如您可從 JAXP 1.0 中所回憶的一樣, SAXParserFactory 類允許對從 factory 返回的 SAXParser 例項進行配置。除了已有的方法( setValidating() 和 setNamespaceAware() )之外,這個新方法允許請求新語法分析器例項的 SAX 2.0 功能。SAX 2.0 提供允許供應商為其語法分析器建立特定功能性的 功能 ,然後,使用者可以與透過 SAX 與這些功能互動。例如,使用者可以請求 功能,該功能允許將 XML 模式驗證開啟或關閉。現在,這可以在 SAXParserFactory 上執行,如清單 4 所示:

  清單 4. 使用 setFeature() 方法

  SAXParserFactory myFactory = SAXParserFactory.newInstance();// Turn on XML Schema validationmyFactory.setFeature("", true);// Now get an instance of the parser with schema validation enabledSAXParser parser = myFactory.newSAXParser();

  當然,還提供了 getFeature() 方法來補充 setFeature() 方法,並允許查詢特定的功能。該方法返回一個簡單的 boolean 值。

  SAX 除了允許設定功能(設定成 true 或 false 值)之外,還允許設定 特性 。在 SAX 中,特性是與實際的 Java 物件相關的名稱。例如,使用 SAX 語法分析器例項,您可以設定特性 ,為該特性分配一個 SAX LexicalHandler 介面的實現。然後,語法分析器將使用該實現來做詞彙處理。因為象詞彙這樣的特性是特定於語法分析器、而不是特定於 factory 的(象特性一樣),所以在 JAXP SAXParser 類中、而不是在 SAXParserFactory 類中提供 setProperty() 方法。與功能一樣,也在 SAXParser 中提供 getProperty() 的補充方法,以返回與特定特性相關的值。

  DOM 中的更新

  JAXP 的 DOM 部分有一些新方法。已經將這些方法新增到現有 JAXP 類中,以同時支援 DOM 級別 2 選項,以及去年出現的一些常見配置情況。我不想在這裡講述所有這些選項及相應方法,因為它們中的很多都不易被察覺(它們只在極罕見的情況下使用),在很多應用中都不需要。當然鼓勵您在最近的 JAXP 規範中聯機檢視這些(請參閱 參考資料 一節)。講完了標準更新、SAX 改動和其它 DOM 方法之後,您可以閱讀 JAXP 1.1 中最重要的改動 -- TRaX API 了。

  TRaX API

  目前為止,已經講了對用 JAXP 進行 XML 語法分析的改動。現在,可以講 JAXP 1.1 中的 XML 變換了。Sun 的 最新版 API 中最令人興奮的進展恐怕就是它允許進行獨立於供應商的 XML 文件變換。如果對 XML 變換和 XSLT(XML 變換)不熟悉,請檢視 dW教程(請參閱 參考資料 )。雖然這種供應商獨立性可以擴充套件目前 JAXP 只作為語法分析 API 的想法,但是它更是個迫切需要的設施,因為目前 XSL 處理器使用不同的方法和方式來允許使用者和開發人員的互動。實際上,不同供應商的 XSL 處理器比 XML 語法分析器有更大的不同。

  最初,JAXP 專家小組試圖提供一個簡單的 Transform 類和幾個方法,以使樣式表和後面的文件變換規範化。這個最初的嘗試後來被證明很不可靠,但是,我很高興地說:我們(JAXP 專家小組)在這之後的嘗試中正取得重大進展。Scott Boag 和 Michael Kay 這兩位當今的 XSL 處理器專家(分別致力於 Apache Xalan 和 SAXON)已經與他人一起開發出 TRaX。它支援更廣範圍的選項和功能,併為幾乎所有的 XML 變換提供完整的支援 -- 所有這些都在 JAXP 支援下工作。

  與 JAXP 的語法分析部分一樣,執行 XML 變換需要三個基本步驟:

  • 獲得 Transformer factory
  • 檢索 Transformer
  • 執行操作(變換)

  使用 factory

  在 JAXP 的變換部分中,使用名為 javax.xml.transform.TransformerFactory 的 factory。這個類與我在第一篇 JAXP 文章和本文前面提到的 SAXParserFactory 和 DocumentBuilderFactory 類相似。當然,只獲得要使用的 factory 例項實在使太簡單了:

  清單 5. 獲得 TransformerFactory 例項

  TransformerFactory factory = TransformerFactory.newInstance();

  一旦獲得 factory 之後,可以在該 factory 上設定各種選項。那些選項將影響由該 factory 建立的 Transformer (稍後就要講)的所有例項。(順便提一句,可以透過 TransformerFactory 獲得 javax.xml.transform.Templates 例項。模板是高階 JAXP 概念,本文不再詳述。)

  首先可以使用的選項是 屬性 。這些不是 XML 屬性,而是與在 XML 語法分析器中講到的特性類似。屬性允許將選項傳遞到底層的 XML 處理器(可能是 Apache Xalan、SAXON 或 Oracle 的 XSL 處理器)。它們高度依賴於供應商。與 JAXP 的語法分析方面類似,還提供 setAttribute() 方法和其搭檔 getAttribute() 。與 setProperty() 類似,前者接受屬性名和 Object 值。與 getProperty() 類似,後者接受屬性名並返回相關的 Object 值。

  設定 ErrorListener 是可用的第二個選項。 ErrorListener 在 javax.xml.transform.ErrorListener 介面中定義,它允許捕獲變換中出現的問題,並在程式中處理。如果熟悉 SAX,您將發現,該介面與 org.xml.sax.ErrorHandler 介面非常類似:

  清單 6. ErrorListener 介面

  package javax.xml.transform;public interface ErrorListener {public void warning(TransformerException exception)throws TransformerException;public void error(TransformerException exception)throws TransformerException;public void fatalError(TransformerException exception)throws TransformerException;}

  透過建立該介面的一個實現,填充三個回撥方法,並在正在使用的 TransformerFactory 例項上使用 setErrorListener() 方法,您將可以處理任何錯誤。

  最後,提供一個方法來設定和檢索由 factory 生成的例項的 URI( uniform resource indicator ,統一資源標識,通常稱為 URL)解析器。在 javax.xml.transform.URIResolver 中定義的介面也與其 SAX 對應介面 org.xml.sax.EntityResolver 類似。該介面有一個方法:

  清單 7. URIResolver 介面

  package javax.xml.transform;public interface URIResolver {public Source resolve(String href, String base)throws TransformerException;}

  該介面在實現之後允許處理在 XML 構造(如 xsl:import 和 xsl:include )中發現的 URI。返回 Source (即將講到)後,可以在遇到特定 URI 時指導變換器在不同位置搜尋指定文件。例如,當遇到包括的 URI 時,可以返回本地文件 oreilly.xsl ,而無需進行網路訪問。可以使用 TransformerFactory 的 setURIResolver() 方法設定 URIResolver 的實現,並使用 getURIResolver() 方法進行檢索。

  最後,一旦設定了想要的選項,就可以透過 factory 的 newTransformer 方法獲得 Transformer 的一個或多個例項。

  清單 8. 獲得 Transformer

  // Get the factoryTransformerFactory factory = TransformerFactory.newInstance();// Configure the factoryfactory.setErrorResolver(myErrorResolver);factory.setURIResolver(myURIResolver);// Get a Transformer to work with, with the options specifiedTransformer transformer = factory.newTransformer(new StreamSource("sheet.xsl"));

  如您所見,該方法將樣式表作為輸入,以在那個 Transformer 例項的所有變換中使用。換句話說,如果要使用樣式表 A 和樣式表 B 變換文件,那麼,將需要兩個 Transformer 例項,每個例項用於一個樣式表。然而,如果要用同一個樣式表變換多個文件,(我們稱其為樣式表 C),那麼將只需要一個與樣式表 C 關聯的 Transformer 例項。

  變換 XML

  有了 Transformer 例項之後,就可以進行實際的 XML 變換了。這包含兩個基本步驟:

  • 設定要使用的 XSL 樣式表
  • 執行變換,指定 XML 文件和結果目標

  如上所述,第一步實際上是最簡單的。在從 factory 獲得 Transformer 例項時必須提供樣式表。必須透過提供 javax.xml.transform.Source 來指定該樣式表的位置。目前為止,您已經在幾個程式碼樣本中見過 Source 介面,該介面是查詢輸入的方式 -- 無論是樣式表、文件還是其它資訊集。TRaX 不僅提供 Source 介面,還提供三個具體的實現:

  • javax.xml.transform.stream.StreamSource
  • javax.xml.transform.dom.DOMSource
  • javax.xml.transform.sax.SAXSource

  這三個中的第一個 StreamSource 從某些 I/O 裝置型別讀取輸入。提供一些構造器來接受 InputStream 、 Reader 或 String 系統標識作為輸入。建立之後,可以將 StreamSource 傳遞到 Transformer 來使用。這可能是最常用的 Source 實現。對於從網路、輸入流或其它某些靜態表示法讀取文件來說,這種方法是非常好的。

  下一個 Source , DOMSource ,允許從現有的 DOM 樹讀取資訊。它提供一個構造器來接收 DOM org.w3c.dom.Node ,然後在使用時從該 Node 讀取。如果已經開始進行語法分析,並且 XML 文件已經在記憶體中以 DOM 結構存在,那麼,這可能是為變換提供現有 DOM 樹的理想方法。

  SAXSource 允許從 SAX 生產者讀取讀取輸入。這種 Source 實現接受 SAX org.xml.sax.InputSource 或 org.xml.sax.XMLReader 作為輸入,並使用來自這些來源的事件作為輸入。對於已經開始使用 SAX,並設定了回撥,而且需要在變換之前觸發回撥的情況,這是理想的方法。

  一旦獲得了 Transformer 的例項(透過提供要在適當的 Source 中使用的樣式表),就可以執行變換了。要完成變換,需如下使用 transform() 方法(沒什麼好奇怪的):

  清單 9. 執行變換

  // Get the factoryTransformerFactory factory = TransformerFactory.newInstance();// Configure the factoryfactory.setErrorResolver(myErrorResolver);factory.setURIResolver(myURIResolver);// Get a Transformer to work with, with the options specifiedTransformer transformer = factory.newTransformer(new StreamSource("sheet.xsl"));// Perform transformation on document A, and print out resulttransfomer.transform(new StreamSource("documentA.xml"),new StreamResult(System.out));

  transform() 方法接受兩個自變數: Source 實現和 javax.xml.transform.Result 實現。您應該已經看到其工作原理的對稱性,並瞭解 Result 介面的功能性。 Source 應該提供要變換的 XML 文件,而 Result 則應該提供變換的輸出目標。與 Source 類似, Result 介面的 TRaX 和 JAXP 提供了三個具體的實現:

  • javax.xml.transform.stream.StreamResult
  • javax.xml.transform.dom.DOMResult
  • javax.xml.transform.sax.SAXResult

  StreamResult 將 OutputStream (與上例中的 System.out 類似)或 Writer 作為構造機制。 DOMResult 將變換輸出到 DOM Node (假設是 DOM org.w3c.dom.Document ),而 SAXResult 將回撥觸發到由已變換的 XML 產生的 SAX ContentHandler 。所有這些都與其對應的 Source 實現類似,您可以透過後者很容易瞭解其用法。

  雖然上例顯示了從流到流的變換,但是,源和結果的任何組合都是可能的。以下是幾個示例:

  清單 10. 各種 TRaX/JAXP 變換

  // Perform transformation on document A, and print out resulttransformer.transform(new StreamSource("documentA.xml"),new StreamResult(System.out));// Transform from SAX and output results to a DOM Nodetransformer.transform(new SAXSource(new InputSource("")),new DOMResult(DocumentBuilder.newDocument()));// Transform from DOM and output to a Filetransformer.transform(new DOMSource(myDomTree),new StreamResult(new FileOutputStream("results.xml")));// Use a custom source and result (JDOM)transformer.transform(new org.jdom.trax.JDOMSource(myJdomDocument),neworg.jdom.trax.JDOMResult(new org.jdom.Document()));

  如您所見,TRaX 在從各種輸入型別到各種輸出型別的變換中,以及在使用各種格式(檔案、記憶體中的 DOM 樹、SAX 讀取器等等)的 XSL 樣式表中,TRaX 可以提供極大的靈活性。

  淺嘗即止

  TRaX 中還有一些其它有用的功能,但是它們不象此處所示的功能那樣常用,而且,本文也沒有足夠篇幅將它們全部列出。建議您在 JAXP 規範包括了 TRaX API 的時候(馬上就要包括)檢視它,它是用於 XML 變換的豐富而強有力的 API。您可以嘗試輸出特性,設定錯誤處理(不僅在 XSL 變換時,而且還在查詢輸入源時),並發現 API 中的各種好東西。開始享受吧,並告訴我們(專家小組)您的想法!

  警告

  在結束之前,還要給出一個警告。如果在三個月以後閱讀本文,下載 JAXP 1.1 ,並得到編譯器和執行時錯誤,請記住,本文是在 JAXP 1.1 將要完成的情況下寫的。與任何早期的發行版一樣,事情總是會發生變化的 -- 甚至在從我的行動式電腦到 developerWorks 生產過程中就會改變。換句話說,這裡所講的方法和功能在我寫本文時是最新的,但是 JAXP 規範可以說仍 處於 變化中。請記住,重要的是本文的概念,而本文所述的方法可能會發生名稱改動,甚至是輕微的行為改變。本文所概述的核心概念仍將以某種形式出現在 JAXP 1.1 中。因此,如果在 JAXP 1.1 的規範和參考實現都完成之時發現細節部分不完全正確,請認為本文所述的細節部分在概念上是正確的。

  結束語

  現在,您知道下一版的 JAXP 將有什麼內容了。最後的規範公開草案應該在 2000 年底完成。實際的參考實現將在不久之後釋出,並在 2001 年第一季度之前完成全部收尾工作。在查詢 JAXP 參考資料時要小心,因為目前的規範草案(到 2000 年 11 月初為止)不包括本文所討論的 TRaX API。在我寫這篇文章時,規範正在修改中,因此,更新的規範將在不久後面世。

  對那些一直在等待使用 JAXP(考慮到 1.0 版的限制,這是個相當明智的轉變)的人來說,現在是開始使用它的時機了。在我的文章和書 Java and XML 中,由於 JAXP 1.0 在 SAX 2.0 和 DOM 級別 2 方面的不足,我對 JAXP 1.1 給予了含混的支援。現在,我高興地承認 JAXP 1.1 是一個重大的進步。Java 和 XML 開發人員將發現,在編寫對 XML 文件進行語法分析和變換的獨立於供應商的程式碼方面,它是不可缺少的工具。那麼,仔細檢視 JAXP 1.0,並使您的應用程式作好準備。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-950144/,如需轉載,請註明出處,否則將追究法律責任。

相關文章