posts - 19,  comments - 4,  trackbacks - 0

          摘要:

          JAXP (全稱Java API for XML Parsing)的可插拔性(pluggability)在開發社區里引起很大的轟動。這點也是JAXP的精華所在。開發人員可以編寫自己的xml處理器,只要它符合JAXP的APIs,這樣底層不同的xml處理器可以任意切換而不用改應用程序的代碼。
          JAXP的演進

          作者:Rahul Srivastava

          譯者:[http://www.matrix.org.cn/user.shtml?username=SJTUer]SJTUer[/url]


          版權聲明:可以任意轉載,轉載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲明
          作者:
          Rahul Srivastava;SJTUer
          原文地址:http://www.xml.com/pub/a/2005/07/06/jaxp.html
          中文地址:http://www.matrix.org.cn/resource/article/43/43893_JAXP.html
          關鍵詞: JAXP XML



          簡介
          在1998年W3CXML1.0推薦標準發布之后,XML就開始變得很流行。Sun公司就是在那時候規范Java Community Process (JCP),同時JAXP(JSR-05)的第一版在2000早些時候發布了。這個版本得到了很多工業集團的支持,譬如(以年月次序排列)BEA Systems, Fujitsu Limited, Hewlett-Packard, IBM, Netscape Communications, Oracle, and Sun Microsystems, Inc.

          JAXP (全稱Java API for XML Parsing)的可插拔性(pluggability)在開發社區里引起很大的轟動。這點也是JAXP的精華所在。開發人員可以編寫自己的xml處理器,只要它符合JAXP的APIs,這樣底層不同的xml處理器可以任意切換而不用改應用程序的代碼。

          那JAXP到底是什么呢?首先 這個P有點迷惑,它代表Parsing還是Processing呢?
          因為JAXP1.0的時候只支持解析(parsing),所以JAXP全稱應該是Java API for XML Parsing.
          但在JAXP1.1的時候,XSL-T被推薦用作XML的轉換(transformation)處理。很遺憾,當時W3C XLT-T的標準規范(specification)里沒有提供任何用來轉換(transformation)處理的APIs。因此JAXP1.1的專家組推薦了一組APIs叫Transformation API for XML (TrAX)。
          從此JAXP就叫Java API for XML Processing. JAXP通過逐步進化,支持的東西也越來越多
          不僅僅是解析xml文件(譬如在解析文檔的時候根據schema校驗有效性,根據預解析的schema來校驗文檔有效性,計算XPath 表達式等等)。

          由于底層用來處理xml文檔的可插拔的processor是任意編寫的,只要它符合JAXP的規范,因此JAXP 是一個輕量級的處理xml文件的處理APIs。(譯者注:JAXP只是一個api規范而已,真正底層實現是任意的。后面會有具體介紹。)

          使用JAXP來解析XML文檔
          JAXP支持基于對象和基于事件的兩種解析方式。基于對象的解析,到目前為止只支持W3C DOM解析,JAXP的專家組可能在JAXP的將來版本中會支持J-DOM規范。基于事件的解析,只有SAX 解析模式被支持,另一個基于事件的解析模式叫Pull Parsing,本來它應該是JAXP的一部分。但是對于Pull Parsing存在有一份不同的JSR (#173)文檔,也就是大家所知道的Streaming API for XML (StAX) parsing,現在我們對于那個也沒什么更多的可以做了。

          image
          Figure 1: Various mechanism of parsing XML

          使用SAX來解析XML文檔
          SAX APIs 是在1998年的早些時候由David Megginson提出的,目標是成為基于事件驅動的xml文檔解析模式的標準API(這里你可以的到一些 SAX 的歷史信息)。即使這樣,SAX仍不是W3C 的REC。但毫無疑問實際中它是行業內解析XML文檔的標準。

          SAX 是一種基于事件的解析模式,是push-parsing原理,解析文檔的時候,當遇到<opening> 標簽, </closing>標簽 或字符等,SAX 都會產生相應的事件(event)。一個SAX解析器解析XML文檔的時候,把文檔看作為一個流,依次產生相應的事件報告給已注冊的content handler, org.xml.sax.ContentHandler,如果有錯誤,錯誤會報告給error handler, org.xml.sax.ErrorHandler.

          如果你不注冊一個error handler,那你就根本不會知道在解析XML文檔的時候有沒有錯誤產生和錯誤是什么。因此,在SAX解析XML文檔的時候注冊一個error handler是極其重要的。

          如果程序需要知道有什么事件產生了(并且想處理此事件),那你必須實現org.xml.sax.ContentHandler 接口并注冊給 SAX解析器。一個典型的事件被觸發的順序是
          startDocument, startElement, characters, endElement, endDocument。
          startDocument 僅僅被觸發一次而且是在觸發其它event之前。同樣,endDocument僅僅被觸發一次而且是在整個文檔被成功解析之后。你可以從SAX javadocs中獲取更詳細的信息。

          image
          Figure 2: SAX Parsing XML

          使用JAXP,通過SAX parse XML document的代碼片斷:
          ?1?????SAXParserFactory?spfactory?=?SAXParserFactory.newInstance();
          ?2?????spfactory.setNamespaceAware(true);
          ?3?????SAXParser?saxparser?=?spfactory.newSAXParser();
          ?4
          ?5?????//write?your?handler?for?processing?events?and?handling?error
          ?6?????DefaultHandler?handler?=?new?MyHandler();
          ?7
          ?8?????//parse?the?XML?and?report?events?and?errors?(if?any)?to?the?handler
          ?9?????saxparser.parse(new?File("data.xml"),?handler);

          文檔對象模型解析
          DOM 解析是基于對象的原理,當用DOM解析XML文檔時它會在內存中生成一個樹形的結構來表示一個XML文檔。樹上的每個節點代表著XML文檔中的一個節點。如果一個DOM解析器符合W3C標準,那它產生的DOM就是W3C的DOM,使用org.w3c.dom APIs就能遍歷和修改這個DOM。

          大部分DOM解析器允許你抽取XML文檔里的一部分來生成DOM樹,而不是把整個XML文檔在內存中建立對應DOM樹。

          image
          Figure 3: DOM Parsing XML


          使用JAXP, 通過DOM parse XML document的代碼片斷:
          ?1?????DocumentBuilderFactory?dbfactory?=?DocumentBuilderFactory.newInstance();
          ?2?????dbfactory.setNamespaceAware(true);
          ?3?????DocumentBuilder?domparser?=?dbfactory.newDocumentBuilder();
          ?4
          ?5?????//parse?the?XML?and?create?the?DOM
          ?6?????Document?doc?=?domparser.parse(new?File("data.xml"));
          ?7
          ?8?????//to?create?a?new?DOM?from?scratch?-
          ?9?????//Document?doc?=?domparser.newDocument();
          10
          11?????//once?you?have?the?Document?handle,?then?you?can?use
          12?????//the?org.w3c.dom.*?APIs?to?traverse?or?modify?the?DOM

          在校驗模式下進行解析
          根據DTD校驗
          DTD 是XML 文檔的語法。經常人們會覺得DTD有點另類,因為它和XML的syntax不一樣,但DTD是W3C XML1.0里的完整的一部分。如果一份XML文檔聲明了DOCTYPE,并且想在解析的時候根據DTD校驗文檔,那你必須在適當的factory里啟用根據DTD校驗文檔(validation)這個特性。例如:
          1??? ?DocumentBuilderFactory?dbfactory?=?DocumentBuilderFactory.newInstance();
          2?????dbfactory.setValidating(true);
          3
          4?????OR
          5
          6?????SAXParserFactory?spfactory?=?SAXParserFactory.newInstance();
          7?????spfactory.setValidating(true);
          8

          注意,如果XML文檔聲明了一個DTD ,即使你不啟用校驗(validation)這個特性,解析器總是試著去讀入這個DTD。??這樣做的目的是為了保證XML文檔中entity reference被正確的擴展了,否則會導致格式不正確的XML文檔,只有在XML文檔序言部分的聲明中standalone屬性被置為true時,外部的DTD才會被完全忽略掉。例如:????
          1<?xml?version="1.1"?encoding="UTF-8"?standalone="yes"?>

          根據W3C Schema來校驗XML文檔(WXS)
          XMLSchema 是XML文檔的另外一種文法描述。XMLSchema非常流行市因為它和XML文檔使用同樣的語法并且提供了豐富的定義校驗限制的特性。如果一個XML文檔用"schemaLocation" 和"noNamespaceSchemaLocation"指向了一個schema,結下來你想啟用根據XMLSchema校驗文檔這個特性,你還要做如下的步驟:
          1.和上面說的一樣,調用SAXParserFactory o或DocumentBuilderFactory的setValidating函數來啟用validation這個特性。
          2.把屬性 "http://java.sun.com/xml/jaxp/properties/schemaLanguage" 值設為 "http://www.w3.org/2001/XMLSchema"

          注意,這種情況下,即使XML文檔有DOCTYPE聲明,處理器仍不會用DTD來校驗這個文檔。但是和前面提到的一樣,為了任何一個entity reference是被正確擴展的,這個DTD還是會被裝載的,
          既然"schemaLocation" 和"noNamespaceSchemaLocation"僅僅是提示,所以可以使用屬性"http://java.sun.com/xml/jaxp/properties/schemaSource"從外部提供schemas來覆蓋這些提示。
          對于這個屬性,一些可以接受值是:

          ·是一個代表schema的URL地址的字符串。
          ·java.io.InputStream with the contents of the schema
          ·org.xml.sax.InputSource
          ·java.io.File
          ·一個 java.lang.Object 的數組,數組內容是上面所提到三類中的一個。
          例如:
          ?1?????SAXParserFactory?spfactory?=?SAXParserFactory.newInstance();
          ?2?????spfactory.setNamespaceAware(true);
          ?3
          ?4?????//turn?the?validation?on
          ?5?????spfactory.setValidating(true);
          ?6
          ?7?????//set?the?validation?to?be?against?WXS
          ?8?????saxparser.setProperty("http://java.sun.com/xml/jaxp/properties/
          ?9?????schemaLanguage",?"http://www.w3.org/2001/XMLSchema");
          10
          11?????//set?the?schema?against?which?the?validation?is?to?be?done
          12?????saxparser.setProperty("http://java.sun.com/xml/jaxp/properties/
          13?????schemaSource",?new?File("myschema.xsd"));

          使用JAXP的TrAX APIs來進行XML文檔轉換處理工作
          W3C XSL-T 定義了一些轉換規則來把源樹轉化生成結果樹。在XSL-T中,轉換信息所存在的文件叫樣式表(stylesheet)。要用JAXP來轉換一個XML文檔,你需要定義一個使用樣式表來轉換XML文檔的轉換器。創建好這樣的轉換器后,它把要轉換的XML文檔作為JAXP的source,返回轉換好的結果作為JAXP的result。目前JAXP提供三種類型的source和result:
          StreamSource, SAXSource, DOMSource and StreamResult, SAXResult, DOMResult, 他們是能夠聯合使用的。

          image
          Figure4: XML Transformation

          從DOM中生成SAX Events:
          ?1?????//parse?the?XML?file?to?a?W3C?DOM
          ?2?????DocumentBuilderFactory?dbfactory?=?DocumentBuilderFactory.newInstance();
          ?3?????dbfactory.setNamespaceAware(true);
          ?4
          ?5?????DocumentBuilder?domparser?=?dbfactory.newDocumentBuilder();
          ?6?????Document?doc?=?domparser.parse(new?File("data.xml"));
          ?7
          ?8??????//prepare?the?DOM?source
          ?9?????Source?xmlsource?=?new?DOMSource(doc);
          10
          11?????//create?a?content?handler?to?handle?the?SAX?events
          12??????ContentHandler?handler?=?new?MyHandler();
          13
          14?????//prepare?a?SAX?result?using?the?content?handler
          15?????Result?result?=?new?SAXResult(handler);
          16
          17?????//create?a?transformer?factory
          18?????TransformerFactory?xfactory?=?TransformerFactory.newInstance();
          19
          20?????//create?a?transformer
          21?????Transformer?xformer?=?xfactory.newTransformer();
          22
          23?????//transform?to?raise?the?SAX?events?from?DOM
          24?????xformer.transform(xmlsource,?result);

          上面的例子中,我們創建Transformer的時候沒有用到XSL。這意味著這個轉換器對XML不會有任何的轉換,source和result是一樣的。當你實際相要用XSL來轉換,你應該創建一個使用了XSL的轉換器,就像下面一樣:
          1?????//create?the?xsl?source
          2??????Source?xslsource?=?new?StreamSource(new?File("mystyle.xsl"));
          3
          4?????//create?the?transformer?using?the?xsl?source
          5?????Transformer?xformer?=?xfactory.newTransformer(xslsource);

          JAXP1.3有哪些新特性?
          除了支持SAX解析,DOM解析,根據DTD/ XMLSchema的校驗,使用XSL-T轉換,
          和以前的版本相比JAXP1.3新支持的特性有:
          1. XML 1.1 和XML 1.1名字空間
          2. XML Inclusions - XInclude 1.0
          3. 根據預解析的schema來校驗文檔。
          4. XPath表達式的計算.
          5.?以前XMLSchema 1.0, XPath 2.0 和XQuery 1.0中的某些數據類型不能被映射到java里的數據類型 ,現在可以了。

          使用JAXP1.3
          XML1.1主要支持的特性如下:
          1.向前兼容不斷增長的Unicode字符集。
          2.在行結束(line-end)字符集中新添加了NEL (#x85)和Unicode行分隔符(#x2028)。

          XML1.1中的變更不是向下兼容的,XML1.0中的一些well-formedness規則在
          XML1.1中可能就不適用。所以XML1.1的規范是全新的而不是從XML1.0規范上升級。
          為了能夠使用XML1.1和XML1.1的名字空間,你必須在XML序言聲明中把version屬性的值設為“1.1”。例如:
          1<?xml?version="1.1"?encoding="UTF-8"?standalone="yes"?>

          XInclude允許一個XML文檔包含另一個XML文檔,例如:
          1??????<myMainXMLDoc?xmlns:xi="http://www.w3.org/2001/XInclude">
          2??????????<xi:include?href="fragment.xml"/>
          3????????????
          4?????</myMainXMLDoc>

          相要使用XML inclusions特性,你必須在適當的factory里設置XInclude屬性為true,就像下面代碼所示:?
          1??????DocumentBuilderFactory?dbfactory?=?DocumentBuilderFactory.newInstance();
          2?????dbfactory.setXIncludeAware(true);

          根據預解析的schema校驗JAXP的輸入源

          javax.xml.validation包提供了解析schema和根據預解析的schema校驗XML文檔的功能。DOMSource和SAXSource是可以根據預解析的schema來被校驗。如果需要可以緩存預解析的schema來達到優化的目的。必須注意到的是,根據預解析的schema校驗JAXP的輸入源中,StreamSource并不是被支持的源,還有schema可以是W3C XML Schema 或者是一個OASIS RELAX-NG。例如:
          ?1?????//parse?an?XML?in?non-validating?mode?and?create?a?DOMSource
          ?2?????DocumentBuilderFactory?dbfactory?=?DocumentBuilderFactory.newInstance();
          ?3?????dbfactory.setNamespaceAware(true);
          ?4?????dbfactory.setXIncludeAware(true);
          ?5
          ?6?????DocumentBuilder?parser?=?dbfactory.newDocumentBuilder();
          ?7?????Document?doc?=?parser.parse(new?File("data.xml"));
          ?8
          ?9?????DOMSource?xmlsource?=?new?DOMSource(doc);
          10
          11?????//create?a?SchemaFactory?for?loading?W3C?XML?Schemas
          12?????SchemaFactory?wxsfactory?=?
          13????????SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
          14
          15?????//set?the?errorhandler?for?handling?errors?in?schema?itself
          16?????wxsfactory.setErrorHandler(schemaErrorHandler);
          17
          18?????//load?a?W3C?XML?Schema
          19?????Schema?schema?=?wxsfactory.newSchema(new?File("myschema.xsd"));
          20
          21?????//?create?a?validator?from?the?loaded?schema
          22?????Validator?validator?=?schema.newValidator();
          23
          24?????//set?the?errorhandler?for?handling?validation?errors
          25?????validator.setErrorHandler(validationErrorHandler);
          26
          27?????//validate?the?XML?instance
          28?????validator.validate(xmlsource);

          計算XPath表達式

          javax.xml.xpath 包提供了根據XML文檔計算XPath表達式的功能。如果一個表達式要被重用,出于性能考慮,這個XPath表達式會被編譯。
          順便說一下,JAXP中的XPath 的API被設計為無狀態的,這就意味著每次你要計算一個XPath表達式,你都要傳入一個XML的文檔。通常,很多XPath表達式是根據單個XML文檔來計算的。這種情況下,如果JAXP中的XPath APIs是有狀態的,XML文檔只需傳入一次,那樣就更好了。對于底層實現來說就多了一個優化選擇,可以把XML 文檔源存儲起來,這樣就可以快速計算XPath表達式了。
          一個根據XML文檔計算XPath表達式得例子:
          1????????<?xml?version="1.0"?>
          2????????<employees>
          3????????????<employee>
          4????????????????<name>e1</name>
          5????????????</employee>
          6????????????<employee>
          7????????????????<name>e2</name>
          8????????????</employee>
          9????????</employees>

          ?1?????//parse?an?XML?to?get?a?DOM?to?query
          ?2?????DocumentBuilderFactory?dbfactory?=?DocumentBuilderFactory.newInstance();
          ?3?????dbfactory.setNamespaceAware(true);
          ?4?????dbfactory.setXIncludeAware(true);
          ?5
          ?6?????DocumentBuilder?parser?=?dbfactory.newDocumentBuilder();
          ?7?????Document?doc?=?parser.parse(new?File("data.xml"));
          ?8
          ?9?????//get?an?XPath?processor
          10?????XPathFactory?xpfactory?=?XPathFactory.newInstance();
          11?????XPath?xpathprocessor?=?xpfactory.newXPath();
          12
          13?????//set?the?namespace?context?for?resolving?prefixes?of?the?Qnames
          14?????//to?NS?URI,?if?the?xpath?expresion?uses?Qnames.?XPath?expression
          15?????//would?use?Qnames?if?the?XML?document?uses?namespaces.
          16?????//xpathprocessor.setNamespaceContext(NamespaceContext?nsContext);
          17
          18?????//create?XPath?expressions
          19?????String?xpath1?=?"/employees/employee";
          20?????XPathExpression?employeesXPath?=?xpathprocessor.compile(xpath1);
          21
          22?????String?xpath2?=?"/employees/employee[1]";
          23?????XPathExpression?employeeXPath??=?xpathprocessor.compile(xpath2);
          24
          25?????String?xpath3?=?"/employees/employee[1]/name";
          26?????XPathExpression?empnameXPath??=?xpathprocessor.compile(xpath3);
          27
          28?????//execute?the?XPath?expressions
          29?????System.out.println("XPath1="+xpath1);
          30?????NodeList?employees?=?(NodeList)employeesXPath.evaluate(doc,?
          31??????????XPathConstants.NODESET);
          32?????for?(int?i=0;?i<employees.getLength();?i++)?{
          33???????????????System.out.println(employees.item(i).getTextContent());
          34?????}

          35
          36?????System.out.println("XPath2="+xpath2);
          37?????Node?employee?=?(Node)employeeXPath.evaluate(doc,?XPathConstants.NODE);
          38?????System.out.println(employee.getTextContent());
          39
          40?????System.out.println("XPath3="+xpath3);
          41?????String?empname?=?empnameXPath.evaluate(doc);
          42?????System.out.println(empname);

          XML和java數據類型間的映射
          Datatypes 在XMLSchema1.0的時候就很流行了,被很多其他XML規范所應用,像 XPath,XQuery,WSDL等。大部分的數據類型可以映射到java的基本數據類型或包裝過的數據類型。其他的類型如:dataTime,duration可以被映射到新的java數據類型: javax.xml.datatype.XMLGregorianCalendar, javax.xml.datatype.Duration, and javax.xml.namespace.QName. 這樣XMLSchema1.0??XPath 2.0 和XQuery 1.0中所有數據類型,JAVA中都有對應的類型存在。

          從可用性角度看,如果DatatypeFactory有方法能夠生成一個對應WXS中數據類型的java對象,并且這個java對象擁有方法能根據facets限制數據類型和根據值來校驗數據類型,這樣就非常好了。
          一個使用Oracle's XDK的例子:
          ?1?????import?oracle.xml.parser.schema.*;
          ?2?????.?.?.
          ?3
          ?4?????//create?a?simpleType?object
          ?5?????XSDSimpleType?st?=?XSDSimpleType.getPrimitiveType(XSDSimpleType.iSTRING);
          ?6
          ?7?????//set?a?constraining?facet?on?the?simpleType
          ?8?????st.setFacet(XSDSimpleType.LENGTH,?"5");
          ?9
          10?????//validate?value
          11?????st.validateValue("hello");

          底層實現間的切換
          一個JAXP的實現通常包括一個默認的解析器,轉換器,xpath引擎和schema校驗器,
          但是就像文章開始的時候所提到的那樣,JAXP是一個可插拔的API,我們可以插入我們自己的處理器來替換JAXP默認的處理器。要實現這樣的切換,我們可以通過設置屬性javax.xml.xxx.yyyFactory的值來指定一個合格的factory的實現類。當yyyFactory.newInstance()被調用的時候,JAXP使用如下的順序查找需要裝載的具體的實現類:

          1.使用javax.xml.xxx.yyyFactory屬性指定的值.
          2.使用 JRE 目錄下lib/jaxp.properties 屬性文件。Jaxp.properties文件只被讀入一次,它的值會被緩存已被將來所用。如果第一次嘗試去讀這個文件而此文件不存在的話,以后就不會嘗試著去檢查此文件是否存在。jaxp.properties里面的值第一次讀過之后,就不可能被修改了。
          3.如果可以地話,可以使用Services API(在JAR的規范里有詳細的信息)來決定哪個實現類被載入。Services API會在runtime時存在的jars的META-INF/services/javax.xml.xxx.yyyFactory里尋找那個classname。
          4.使用平臺默認的javax.xml.xxx.yyyFactory 實例。
          javax.xml.xxx.yyyFactory 可是下面其中的一個:
          javax.xml.parsers.SAXParserFactory
          javax.xml.parsers.DocumentBuilderFactory
          javax.xml.transform.TransformerFactory
          javax.xml.xpath.XPathFactory
          javax.xml.validation.SchemaFactory:schemaLanguage (schemaLanguage 是調用SchemaFactory的newInstance函數時所提供的參數)

          例如:想在JAXP中使用SAX解析器,你可以用上面提到的4個方法中的任何一個把
          javax.xml.parsers.SAXParserFactory設為org.apache.xerces.jaxp.SAXParserFactoryImpl。
          其中的一個方法如下:?
          1?????java?-Djavax.xml.parsers.SAXParserFactory=??????????org.apache.xerces.jaxp.SAXParserFactoryImpl?MyApplicationProgram
          posted on 2007-01-04 08:54 公主她爹 閱讀(393) 評論(0)  編輯  收藏 所屬分類: Java技術

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           

          <2007年1月>
          31123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          常用鏈接

          留言簿(1)

          隨筆分類(19)

          隨筆檔案(19)

          相冊

          娛樂網站

          技術網站(Java)

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 祥云县| 和田市| 将乐县| 济宁市| 嘉荫县| 礼泉县| 灌云县| 邹城市| 耿马| 固阳县| 泗水县| 揭西县| 龙门县| 城口县| 汕尾市| 郧西县| 利川市| 义马市| 兴城市| 白沙| 油尖旺区| 旬邑县| 乡城县| 岳西县| 西乌珠穆沁旗| 湖口县| 阿尔山市| 钟祥市| 锡林浩特市| 克拉玛依市| 会理县| 敖汉旗| 新邵县| 昌吉市| 郓城县| 宾川县| 太保市| 秦皇岛市| 田东县| 本溪| 班玛县|