2008-09-23 71 views
35

我有開始這樣一個XML文件來檢索XML文件的命名空間:如何使用XPath

<Elements name="Entities" xmlns="XS-GenerationToolElements"> 

我不得不打開很多這些文件。其中每一個都有不同的命名空間,但一次只有一個命名空間(我永遠不會在一個xml文件中找到兩個命名空間)。

使用XPath我希望有一種自動的方式將給定的名稱空間添加到名稱空間管理器。 到目前爲止,我只能通過解析XML文件來獲取名稱空間,但我有一個XPathNavigator實例,它應該有一個很好而乾淨的方法來獲取名稱空間,對吧?

- 或 -

鑑於我只有一個命名空間,以某種方式使的XPath使用的唯一一個出現在XML中,從而避免總是附加命名空間混亂的代碼。

+0

在默認名稱空間始終是誰的?還是你曾經有: xmlns:myns =「namespace-uri」 你也讀完整個文件到一個DOM文檔或解析使用像XmlValidatingReader? – Kev 2008-09-23 17:36:27

+0

他們總是在默認名稱空間。 我還沒有完全讀完這個文件,因爲我在這個問題上陷入困境;我想我不是完全理解你什麼時候問「到一個DOM文檔或使用像XmlValidatingReader解析」;我只會使用XPath來閱讀XML;它不好嗎? – 2008-09-23 18:22:38

回答

76

有幾種技巧可以嘗試;您使用哪種方法將取決於您需要從文檔中獲取哪些信息,您希望的嚴格程度以及您使用的XPath實施的一致性。

獲取與特定前綴關聯的名稱空間URI的一種方法是使用軸namespace::。這將爲您提供一個名稱空間節點,其名稱是前綴,其值是名稱空間URI。例如,你可以得到使用路徑的文檔元素上的默認命名空間URI:

/*/namespace::*[name()=''] 

你也許可以用它來建立的命名空間協會爲您的XPathNavigator。不過要注意的是,namespace::軸是XPath 1.0的其中一個角落,並不總是被實現。

獲得該名稱空間URI的第二種方法是在文檔元素上使用namespace-uri()函數(您所說的將始終在該名稱空間中)。表達式:

namespace-uri(/*) 

會給你那個名字空間。

另一種方法是忘記將前綴與該名稱空間關聯,並使路徑無名稱空間。您可以通過使用local-name()函數來執行此操作,無論何時您需要引用其名稱空間不明的元素。例如:

//*[local-name() = 'Element'] 

你可以走一步,並測試對文檔元素的一個元素的命名空間URI,如果你真的想:

//*[local-name() = 'Element' and namespace-uri() = namespace-uri(/*)] 

最後一個選擇,因爲命名空間似乎對你來說毫無意義,將通過一個過濾器去除命名空間來運行你的XML。那麼您就不必擔心XPath中的這些問題。要做到這一點,最簡單的方法是簡單地使用正則表達式刪除xmlns屬性,但你可以做一些事情,如果你需要同時做其他的整理更加複雜。

4

不幸的是,XPath沒有任何「默認命名空間」的概念。您需要使用XPath上下文註冊帶有前綴的名稱空間,然後在XPath表達式中使用這些前綴。它意味着非常詳細的xpath,但它是XPath 1的一個基本缺陷。很顯然,XPath 2將解決這個問題,但現在對你來說這沒用。

我建議您以編程方式檢查您的XML文檔的名稱空間,將該名稱空間與XPath上下文中的前綴相關聯,然後在xpath表達式中使用前綴。

+0

看來它將不得不歸結爲..!由於 – 2008-09-23 18:30:36

+0

我懷疑這是actualy答案,因爲它似乎是這種願望,以避免在查詢的XPath命名空間的增加複雜性。不要忘記接受適當的答案。 – AnthonyWJones 2008-09-23 21:25:29

10

這40行XSLT轉換提供了所有在一個給定的XML文檔在名稱空間中的有用信息:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:ext="http://exslt.org/common" 
    exclude-result-prefixes="ext" 
> 

<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:strip-space elements="*"/> 

<xsl:key name="kNsByNsUri" match="ns" use="@uri"/> 

<xsl:variable name="vXmlNS" 
    select="'http://www.w3.org/XML/1998/namespace'"/> 

<xsl:template match="/"> 
    <xsl:variable name="vrtfNamespaces"> 
    <xsl:for-each select= 
     "//namespace::* 
      [not(. = $vXmlNS) 
      and 
       . = namespace-uri(..) 
      ]"> 
     <ns element="{name(..)}" 
      prefix="{name()}" uri="{.}"/> 
    </xsl:for-each> 
    </xsl:variable> 

    <xsl:variable name="vNamespaces" 
    select="ext:node-set($vrtfNamespaces)/*"/> 

    <namespaces> 
      <xsl:for-each select= 
      "$vNamespaces[generate-id() 
         = 
         generate-id(key('kNsByNsUri',@uri)[1]) 
         ]"> 
      <namespace uri="{@uri}"> 
       <xsl:for-each select="key('kNsByNsUri',@uri)/@element"> 
       <element name="{.}" prefix="{../@prefix}"/> 
       </xsl:for-each> 
      </namespace> 
      </xsl:for-each> 
    </namespaces> 
</xsl:template> 

當在下面的XML文檔應用:

<a xmlns="my:def1" xmlns:n1="my:n1" 
    xmlns:n2="my:n2" xmlns:n3="my:n3"> 
    <b> 
    <n1:d/> 
    </b> 
    <n1:c> 
    <n2:e> 
     <f/> 
    </n2:e> 
    </n1:c> 
    <n2:g/> 
</a> 

想要的結果產生:

<namespaces> 
    <namespace uri="my:def1"> 
     <element name="a" prefix=""/> 
     <element name="b" prefix=""/> 
     <element name="f" prefix=""/> 
    </namespace> 
    <namespace uri="my:n1"> 
     <element name="n1:d" prefix="n1"/> 
     <element name="n1:c" prefix="n1"/> 
    </namespace> 
    <namespace uri="my:n2"> 
     <element name="n2:e" prefix="n2"/> 
     <element name="n2:g" prefix="n2"/> 
    </namespace> 
</namespaces>