2010-09-19 51 views
10

我正在編寫一個XSLT 1.0樣式表來將多名稱空間XML文檔轉換爲HTML。在結果HTML中的某個地方,我想列出文檔中出現的所有命名空間。XSLT:如何獲取所有使用的名稱空間的列表

這可能嗎?

我想過像

<xsl:for-each select="//*|//@*"> 
    <xsl:value-of select="namespace-uri(.)" /> 
</xsl:for-each> 

但我當然會得到重複的gazillions。所以我必須以某種方式過濾,我已經打印了。

遞歸調用模板可以工作,但我無法將頭圍繞如何到達所有元素。

直接訪問//@xmlns:*不起作用,因爲無法通過XPath訪問它(不允許將任何前綴綁定到xmlns:名稱空間)。

+0

問得好(+1)。看到我的答案是一個簡短而有效的解決方案。 :) – 2010-09-19 19:41:36

+0

我剛剛添加到我的答案XSLT 2.0解決方案 - 只是爲了完整性。 – 2010-09-20 12:46:12

回答

7

另一個不帶擴展功能:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="*"> 
     <xsl:param name="pNamespaces" select="'&#xA;'"/> 
     <xsl:variable name="vNamespaces"> 
      <xsl:variable name="vMyNamespaces"> 
       <xsl:value-of select="$pNamespaces"/> 
       <xsl:for-each select="namespace::* 
             [not(contains(
               $pNamespaces, 
               concat('&#xA;',.,'&#xA;')))]"> 
        <xsl:value-of select="concat(.,'&#xA;')"/> 
       </xsl:for-each> 
      </xsl:variable> 
      <xsl:variable name="vChildsNamespaces"> 
       <xsl:apply-templates select="*[1]"> 
        <xsl:with-param name="pNamespaces" 
             select="$vMyNamespaces"/> 
       </xsl:apply-templates> 
      </xsl:variable> 
      <xsl:value-of select="concat(substring($vMyNamespaces, 
                1 div not(*)), 
             substring($vChildsNamespaces, 
                1 div boolean(*)))"/> 
     </xsl:variable> 
     <xsl:variable name="vFollowNamespaces"> 
      <xsl:apply-templates select="following-sibling::*[1]"> 
       <xsl:with-param name="pNamespaces" select="$vNamespaces"/> 
      </xsl:apply-templates> 
     </xsl:variable> 
     <xsl:value-of 
      select="concat(substring($vNamespaces, 
             1 div not(following-sibling::*)), 
          substring($vFollowNamespaces, 
             1 div boolean(following-sibling::*)))"/> 
    </xsl:template> 
</xsl:stylesheet> 

輸出(帶Dimitre的輸入樣本):

http://www.w3.org/XML/1998/namespace 
mynamespace 
mynamespace2 
mynamespace3 

EDIT:也在該XPath表達式:

//*/namespace::*[not(. = ../../namespace::*|preceding::*/namespace::*)] 

作爲證明,這個樣式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:template match="/"> 
     <xsl:for-each select="//*/namespace::* 
            [not(. = ../../namespace::*| 
               preceding::*/namespace::*)]"> 
      <xsl:value-of select="concat(.,'&#xA;')"/> 
     </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

輸出:

http://www.w3.org/XML/1998/namespace 
mynamespace 
mynamespace2 
mynamespace3 

EDIT 4:相同高效作爲二階段的轉變。

這個樣式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:key name="kElemByNSURI" 
      match="*[namespace::*[not(. = ../../namespace::*)]]" 
       use="namespace::*[not(. = ../../namespace::*)]"/> 
    <xsl:template match="/"> 
     <xsl:for-each select= 
      "//namespace::*[not(. = ../../namespace::*)] 
          [count(..|key('kElemByNSURI',.)[1])=1]"> 
      <xsl:value-of select="concat(.,'&#xA;')"/> 
     </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

輸出:

http://www.w3.org/XML/1998/namespace 
mynamespace 
mynamespace2 
mynamespace3 

編輯 5:當你正在處理一個XSLT處理器沒有namespace斧地執行(如TransforMiix),你只能提取命名空間實際與此樣式表一起使用:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:key name="kElemByNSURI" match="*|@*" use="namespace-uri()"/> 
    <xsl:template match="/"> 
     <xsl:for-each select= 
      "(//*|//@*)[namespace-uri()!=''] 
         [count(.|key('kElemByNSURI',namespace-uri())[1])=1]"> 
      <xsl:value-of select="concat(namespace-uri(),'&#xA;')"/> 
     </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

TransforMiix輸出:

mynamespace2 
+0

@Alejandro:+1爲正確的答案。我們都知道,不使用'xxx:node-set()'會導致效率更低的解決方案:) – 2010-09-19 22:47:44

+0

+1,用於避免擴展的解決方案。我正在研究一個使用字符串來累積命名空間URI的方法,但你的方法可能會更好。 – LarsH 2010-09-20 02:02:13

+0

感謝您的解決方案!我錯過了'namespace ::'軸,並且我總是忘記''(特別是當它的使用非常有價值時)。 – Boldewyn 2010-09-20 06:32:53

5

這種轉變

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="text"/> 
<xsl:template match="/"> 
    <xsl:for-each select= 
    "//namespace::*[not(. = ../../namespace::*)]"> 
    <xsl:value-of select="concat(.,'&#xA;')"/> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

當這個XML文檔施加:

<authors xmlns:user="mynamespace"> 
    <?ttt This is a PI ?> 
    <author xmlns:user2="mynamespace2"> 
    <name idd="VH">Victor Hugo</name> 
    <user2:name idd="VH">Victor Hugo</user2:name> 
    <nationality xmlns:user3="mynamespace3">French</nationality> 
    </author> 
</authors> 

產生想要的,正確的結果

http://www.w3.org/XML/1998/namespace 
mynamespace 
mynamespace2 
mynamespace3 

更新

作爲@svick曾評論,將上述溶液仍然會偶爾產生重複的名稱空間,如用下面的XML文檔:

<authors xmlns:user="mynamespace"> 
    <?ttt This is a PI ?> 
    <author xmlns:user2="mynamespace2"> 
    <name idd="VH">Victor Hugo</name> 
    <user2:name idd="VH">Victor Hugo</user2:name> 
    <nationality xmlns:user3="mynamespace3">French</nationality> 
    </author> 
    <t xmlns:user2="mynamespace2"/> 
</authors> 

命名空間"mynamespace2"將在輸出產生兩次。

以下改造修復此問題

<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 method="text"/> 

<xsl:key name="kNSbyURI" match="n" use="."/> 

<xsl:template match="/"> 
    <xsl:variable name="vrtfNS"> 
     <xsl:for-each select= 
     "//namespace::*[not(. = ../../namespace::*)]"> 
     <n><xsl:value-of select="."/></n> 
     </xsl:for-each> 
    </xsl:variable> 

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

    <xsl:for-each select= 
    "$vNS[generate-id() 
     = 
      generate-id(key('kNSbyURI',.)[1]) 
     ]"> 
    <xsl:value-of select="concat(., '&#xA;')"/> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

當這種轉變是在上面的XML文檔應用,它產生的只是所有唯一的命名空間的文件在:

http://www.w3.org/XML/1998/namespace 
mynamespace 
mynamespace2 
mynamespace3 

II部分:一個XSLT 2.0溶液

XSLT 2.0解決方案是一個簡單的XPath 2。0單行:

distinct-values(//namespace::*) 
+0

當在兩個位置定義了一個名稱空間時,這不起作用:'<節點xmlns =「ns」/><節點xmlns =「ns」/>'。 – svick 2010-09-19 20:15:41

+0

@svick:趕上!我現在已經解決了這個問題。 – 2010-09-19 20:38:44

+0

感謝您的回答!我正在尋找一個非EXSLT解決方案,因爲我需要該模板是交叉引擎可執行的。但是如果我將來會在XSLT 2.0中重寫它,我會回來討論節點集解決方案。 – Boldewyn 2010-09-20 06:31:38

相關問題