byterat

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            54 隨筆 :: 0 文章 :: 15 評論 :: 0 Trackbacks

          玩轉 XPath 和缺省命名空間(Default Namespaces)

          原文出自:http://www.edankert.com/defaultnamespaces.html
          翻譯文出自:http://wakan.blog.51cto.com/blog/59583/7220



          諸如“為什么用 XPath 的表達式進行查詢,卻沒有返回所期望的結果?”的問題通常都與命名空間(NameSpace)有關,而且絕大多數是與缺省命名空間(Default Namespace)有關。本文試圖解釋這個問題并針對三種流行的 XPath 實現給出解決方法:Jaxen、JAXP XPPathFactory 以及 XSLT。
          內容列表
          問題描述
          “前綴-命名空間”映射
          Jaxen 和 Dom4J
          Jaxen 和 XOM
          Jaxen 和 JDOM
          JAXP XPathFactory
          XSLT
          結束語
          資源


          問題描述
          看下述 XML:
          <catalog>
          ? <cd>
          ??? <artist>Sufjan Stevens</artist>
          ??? <title>Illinois</title>
          ??? <src>http://www.sufjan.com/</src>
          ? </cd>
          ? <cd>
          ??? <artist>Stoat</artist>
          ??? <title>Future come and get me</title>
          ??? <src>http://www.stoatmusic.com/</src>
          ? </cd>
          ? <cd>
          ??? <artist>The White Stripes</artist>
          ??? <title>Get behind me satan</title>
          ??? <src>http://www.whitestripes.com/</src>
          ? </cd>
          </catalog>

          ??? 你可以使用“//cd”來得到沒有在任何命名空間中定義的“cd”節點。?


          ??? 現在讓我們來改造這個 XML,讓它的所有元素都屬于 'http://www.edankert.com/examples/' 命名空間中。
          ?
          ??? 為了避免在每個不同的元素前都要加個前綴,我們在根元素上定義通常所說的缺省命名空間。改造后的 XML 如下:
          ?
          <catalog xmlns="
          http://www.edankert.com/examples/ ">
          ? <cd>
          ??? <artist>Sufjan Stevens</artist>
          ??? <title>Illinois</title>
          ??? <src>http://www.sufjan.com/</src>
          ? </cd>
          ? <cd>
          ??? <artist>Stoat</artist>
          ??? <title>Future come and get me</title>
          ??? <src>http://www.stoatmusic.com/</src>
          ? </cd>
          ? <cd>
          ??? <artist>The White Stripes</artist>
          ??? <title>Get behind me satan</title>
          ??? <src>http://www.whitestripes.com/</src>
          ? </cd>
          </catalog>

          ??? 當我們使用與上文相同的 XPath “//cd”,將得不到任何元素。這是因為指定的 XPath 返回的是所有不屬于任何命名空間的“cd”節點,而本例中,所有的“cd”元素都屬于缺省的命名空間“http://www.edankert.com/examples/”。


          “前綴-命名空間”映射
          ??? 為了取出命名空間“http://www.edankert.com/examples/”中的所有“cd”元素,我們需要對 XPath 表達式做一些額外的工作。
          ?
          ??? 為了解決這個問題,XPath 規范允許我們使用 QName 來指定元素或者屬性。QName 可以是元素的直接名稱(形如“element”),或者包含一個前綴(形如“pre:element”)。這個前綴需要映射到一個命名空間的 URI 上。例如,如果把“pre”前綴映射到“http://www.edankert.com/test”上,則通過“pre:element”可以查找出屬于命名空間“http://www.edankert.com/test”的所有 “element”元素。
          ?
          ??? 在本例中,我們把“edx”映射到“'http://www.edankert.com/examples/”命名空間上。通過 XPath“//edx:cd”就可以查找出屬于“'http://www.edankert.com/examples/”命名空間的所有“cd”元素。?
          ?
          ??? XPath 處理器允許設置“前綴-命名空間”的映射,但是,如何去映射,卻要依賴于具體的實現。下文舉例說明 Jaxen (JDOM/dom4j/XOM)、JAXP 以及 XSLT 中是如何進行“前綴-命名空間”的映射的。

          Jaxen 和 Dom4J
          ??? 下述代碼從文件系統讀入一個 XML 文件到 org.dom4j.Document 對象中,并且在 Document 中查找屬于“http://www.edankert.com/examples/”命名空間的所有“cd”元素。
          ?
          try {
          ? SAXReader reader = new SAXReader();
          ? Document document = reader.read( "file:catalog.xml");
          ?
          ? HashMap map = new HashMap();
          ? map.put( "edx", "
          http://www.edankert.com/examples/ ");
          ?
          ? XPath xpath = new Dom4jXPath( "http://edx:cd");
          ? xpath.setNamespaceContext( new SimpleNamespaceContext( map));
          ?
          ? List nodes = xpath.selectNodes( document);
          ?
          ? ...
          ?
          } catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( DocumentException e) {
          ? // the document is not well-formed.
          ? ...
          }
          ??? 第一步,創建一個 SAXReader,用來從文件系統中讀取“catalog.xml”并創建一個特定于 Dom4j 的 Document 對象。

          ??? 第二步,對于所有 Jaxen 實現都一樣,就是創建一個 HashMap 對象,用于保存“前綴-命名空間的 URI”的映射。
          ?
          ??? 為了能通過 Dom4j 使用 Jaxen 的 XPath 功能,需要創建一個與 Dom4j 相關的 XPath 對象:Dom4jXPath。創建方法是把 XPath 的表達式(即“//edx:cd”)傳給 Dom4jXPath 的構造方法。
          ?
          ??? 現在,我們已經創建了 XPath 對象,接下來可以把“前綴-命名空間”的映射表傳遞給 XPath 引擎:把這個 HashMap 映射表用 SimpleNamespaceContext 包裝起來。SimpleNamespaceContext 是 Jaxen 的 NamespaceContext 接口的默認實現類。
          ?
          ??? 最后一步就是調用 XPath 對象的 selectNodes() 方法進行查找。并把完整的 Dom4j? Document 對象作為參數傳遞進去。實際上,Document 中的任何結點都可以作為參數。

          Jaxen 和 XOM
          ??? XOM 是基于簡單的 Java DOM APIs 之上的最新工具,它的設計初衷是提供簡單和易學易用的接口。
          ?
          try {
          ? Builder builder = new Builder();
          ? Document document = builder.build( "file:catalog.xml");
          ?
          ? HashMap map = new HashMap();
          ? map.put( "edx", "
          http://www.edankert.com/examples/ ");
          ?
          ? XPath xpath = new XOMXPath( "http://edx:cd");
          ? xpath.setNamespaceContext( new SimpleNamespaceContext( map));
          ?
          ? List nodes = xpath.selectNodes( document);
          ?
          ? ...
          ?
          } catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
          ? // An error occurred opening the document
          ? ...
          } catch ( ParsingException e) {
          ? // An error occurred parsing the document
          ? ...
          }

          ?
          ??? 我們需要創建一個 Builder 對象,從文件系統中讀取“catalog.xml”文件,并創建出與 XOM 相關的 Document 對象。
          ?
          ??? 下一步創建出包含了“前綴-命名空間”映射關系的 HashMap 對象。
          ?
          ??? 我們需要創建一個特定于 XOM 的 XPath 對象:XOMXPath。創建方法是把 XPath 表達式傳遞給構造方法,然后就可以通過 XOM 使用 Jaxen 的 XPath 功能了。
          ?
          ??? 創建完 XPath 對象后,同樣,我們把“前綴-命名空間”的映射表用 SimpleNamespaceContext 對象封裝后,傳遞給 XPath 引擎。
          ?
          ??? 最后調用 XPath 對象的“selectNodes()”方法進行查找,把 XOM Document 對象作為本方法的參數。
          Jaxen 和 JDOM
          ??? JDOM 是第一個提供簡單的 XML 訪問 API 的工具。
          ?
          try {
          ? SAXBuilder builder = new SAXBuilder();
          ? Document document = builder.build( "file:catalog.xml");
          ?
          ? HashMap map = new HashMap();
          ? map.put( "edx", "
          http://www.edankert.com/examples/ ");
          ?
          ? XPath xpath = new JDOMXPath( "http://edx:cd");
          ? xpath.setNamespaceContext( new SimpleNamespaceContext( map));
          ?
          ? List nodes = xpath.selectNodes( document);
          ?
          ? ...
          ?
          } catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
          ? // An error occurred opening the document
          ? ...
          } catch ( JDOMException e) {
          ? // An error occurred parsing the document
          ? ...
          }

          ?
          ??? 首先,通過 SAXBuilder 創建了一個特定于 JDom 的 Document 對象。
          ?
          ??? 接著創建一個特定于 JDOM 的 XPath 對象:JDOMXPath。
          ?
          ??? 然后,把“前綴-命名空間”的映射表(HashMap)用 SimpleNamespaceContext 對象封裝起來,傳遞給 XPath 引擎。
          ?
          ??? 最后調用 XPath 對象的“selectNodes()”方法來進行查找,并把 JDOM 的 Document 對象作為本方法的輸入參數。
          JAXP XPathFactory
          ??? 從 1.3 版起, JAXP 還提供了一種在 XML Object Models 上進行查詢的通用機制。
          ?
          try {
          ?DocumentBuilderFactory domFactory =DocumentBuilderFactory.newInstance();
          ? domFactory.setNamespaceAware( true);
          ?
          ?DocumentBuilder builder = domFactory.newDocumentBuilder();Document document = builder.parse( new InputSource( "file:catalog.xml"));
          ?
          ?XPathFactory factory =XPathFactory.newInstance();
          ?XPath xpath = factory.newXPath();
          ? xpath.setNamespaceContext( new NamespaceContext() {
          ??? public String getNamespaceURI(String prefix) {
          ????? if ( prefix.equals( "edx")) {
          ??????? return "
          http://www.edankert.com/examples/ ";
          ????? } else if ...
          ??????? ...
          ????? }
          ?????
          ????? return XPathConstants.NULL_NS_URI;
          ??? }
          ?
          ??? public String getPrefix(String namespaceURI) {
          ????? if ( namespaceURI.equals( "
          http://www.edankert.com/examples/ ")) {
          ??????? return "edx";
          ????? } else if ...
          ??????? ...
          ????? }?
          ???
          ????? return null;
          ??? }
          ?
          ??? public Iterator getPrefixes(String namespaceURI) {
          ???? ArrayList list = new ArrayList();
          ???
          ????? if ( namespaceURI.equals( "
          http://www.edankert.com/examples/ ")) {
          ??????? list.add( "edx");
          ????? } else if ...
          ??????? ...
          ????? }
          ???
          ????? return list.iterator();
          ??? }
          ? });
          ?
          ?Object nodes = xpath.evaluate( "http://edx:cd", document.getDocumentElement(),
          ??????????????????????????????? XPathConstants.NODESET);
          ?
          ? ...
          ?
          } catch (ParserConfigurationException e) {
          ? ...
          } catch (XPathExpressionException e) {
          ? ...
          } catch (SAXException e) {
          ? ...
          } catch (IOException e) {
          ? ...
          }

          ??? 首先用 JAXP 的 DocumentBuilderFactory 創建一個org.w3c.dom.Document 對象,確保啟用了 namespace 處理功能。
          ?
          ??? 現在可以通過 XPathFactory 來創建 XPath 對象,并通過 XPath 對象對文檔進行查詢。
          ?
          ??? 為了創建“前綴-命名空間”映射并傳遞給 XPath 引擎,我們需要實現 NamespaceContext 接口,該接口目前還沒有默認實現類。這就意味著要實現 getNamespaceURI、getPrefix 和getPrefixes 方法,并確保這些方法能返回正確的值,包括“xmlns”和“xml”前綴所對應的命名空間的 URI 值。
          ?
          ??? 把我們自己實現的 NamespaceContext 對象傳遞給 XPath 引擎后,就可以通過 evaluate 方法來查詢 XPath 表達式所對應的元素:使用上文中提到的 XPath 表達式,并使用 Document 的根節點作為輸入入參數,并接收一個 NodeList 對象作為返回結果。
          XSLT
          ??? XPath 設計的初衷是用于 XSLT。這也許能解釋“為什么在 XSLT 中定義命名空間的前綴是一件很平常的事”(也許因為 XSLT 也是一個 XML 名詞的緣故吧)。
          ?
          <xsl:stylesheet version="1.1" xmlns:xsl="
          http://www.w3.org/1999/XSL/Transform ">
          ? <xsl:template match="http://edx:cd" xmlns:edx="
          http://www.edankert.com/examples/ ">
          ??? <xsl:apply-templates/>
          ? </xsl:template>
          </xsl:stylesheet>
          ?
          ??? 只需要使用 XML 本身的機制,簡單地為 edx 前綴賦予一個命名空間的 URI 值。
          ?
          ??? 通過與我們的 XPath 表達式“//edx:cd”相匹配的 xsl:template,能得到與上文其他例子相同的輸出結果。

          結束語
          ??? 為了在(缺省)命名空間上使用 XPath 表達式,我們需要指定一個“前綴-命名空間”映射。正如我們所看到的,具體使用什么樣的前綴名稱,是無關緊要的。
          ?
          ??? 同樣的方法,也可以用于查詢那些用其他前綴修飾的元素。這意味著上面的例子對下述 XML 也有效。下述 XML 沒有使用缺省命名空間,而是使用了 examples 作命名空間的前綴:
          ?
          <examples:catalog xmlns:examples="
          http://www.edankert.com/examples/ ">
          ? <examples:cd>
          ??? <examples:artist>Sufjan Stevens</examples:artist>
          ??? <examples:title>Illinois</examples:title>
          ??? <examples:src>http://www.sufjan.com/</examples:src>
          ? </examples:cd>
          ? <examples:cd>
          ??? <examples:artist>Stoat</examples:artist>
          ??? <examples:title>Future come and get me</examples:title>
          ??? <examples:src>http://www.stoatmusic.com/</examples:src>
          ? </examples:cd>
          ? <examples:cd>
          ??? <examples:artist>The White Stripes</examples:artist>
          ??? <examples:title>Get behind me satan</examples:title>
          ??? <examples:src>http://www.whitestripes.com/</examples:src>
          ? </examples:cd>
          </examples:catalog>

          ?
          ??? 使用“//edx:cd”作為 XPath 表達式,使用與前文例子相同的“前綴-命名空間”映射,在這個 XML 上同樣能查詢出屬于“http://www.edankert.com/examples/”命名空間的所有“cd”元素。
          資源
          Extensible Markup Language (XML) 1.0 (Third Edition)
          http://www.w3.org/TR/REC-xml/
          Namespaces in XML
          http://www.w3.org/TR/REC-xml-names/
          XML Path Language (XPath) Version 1.0
          http://www.w3.org/TR/xpath
          XSL Transformations (XSLT) Version 1.0
          http://www.w3.org/TR/xslt
          dom4j
          http://www.dom4j.org/
          XOM
          http://www.xom.nu/
          JDOM
          http://www.jdom.org/
          Jaxen
          http://www.jaxen.org/
          Java 5.0
          http://java.sun.com/j2se/1.5.0/

          posted on 2007-03-21 10:13 比特鼠 閱讀(1728) 評論(1)  編輯  收藏 所屬分類: Java

          評論

          # re: (轉)玩轉 XPath 和缺省命名空間(Default Namespaces) 2007-04-20 10:07 小妖
          內容不錯啊!轉走了啊!
          謝謝啦~~  回復  更多評論
            

          主站蜘蛛池模板: 三江| 南岸区| 昭苏县| 龙川县| 桃源县| 应城市| 观塘区| 涞水县| 乐清市| 平和县| 文登市| 垫江县| 阳西县| 郸城县| 建始县| 菏泽市| 天台县| 乌拉特中旗| 凤凰县| 滕州市| 卢龙县| 岫岩| 梅州市| 兴和县| 吉林省| 玉林市| 九寨沟县| 凤台县| 万州区| 通许县| 旬阳县| 黎川县| 永昌县| 盖州市| 建阳市| 马山县| 叙永县| 靖西县| 班戈县| 长岭县| 普陀区|