2009-06-04 10 views
24

XSLTでは、要素を処理するときにXML文書のどこにあるのかを判断する方法はありますか?XSLTでどのように現在の要素パスを出力しますか?

例:私のコンテキストは要素「Ele111」であればXSLTで

<Doc> 
    <Ele1> 
    <Ele11> 
     <Ele111> 
     </Ele111> 
    </Ele11> 
    </Ele1> 
    <Ele2> 
    </Ele2> 
</Doc> 

...以下のXMLドキュメントフラグメントを考えると、どのように私は、出力のフルパスをXSLTを得ることができますか? "/ Doc/Ele1/Ele11/Ele111"と出力したいと思います。

この質問の文脈:私は徹底的に(一般的には再帰を使用して)トラバースしたい非常に大きな非常に深いドキュメントを持っています。特定の属性を持つ要素を見つけたら、 。私はトラバースしながら私の現在の道を歩むことができると思うが、私はXSLT/XPathが知っているべきだと思うだろう。

答えて

6

これはXPathに組み込まれていないと思われますが、この例をベースにしたhereのような再帰的テンプレートが必要になることがあります。 XML文書のすべての要素を処理し、その要素へのパスを記述したものと同様のスタイルで出力します。

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
     xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     exclude-result-prefixes="xs" 
     version="2.0"> 

    <xsl:template match="/"> 
     <paths> 
      <xsl:apply-templates/> 
     </paths> 
    </xsl:template> 

    <xsl:template match="//*"> 
     <path> 
     <xsl:for-each select="ancestor-or-self::*"> 
      <xsl:call-template name="print-step"/> 
     </xsl:for-each> 
     </path> 
     <xsl:apply-templates select="*"/> 
    </xsl:template> 

    <xsl:template name="print-step"> 
     <xsl:text>/</xsl:text> 
     <xsl:value-of select="name()"/> 
     <xsl:text>[</xsl:text> 
     <xsl:value-of select="1+count(preceding-sibling::*)"/> 
     <xsl:text>]</xsl:text> 
    </xsl:template> 

</xsl:stylesheet> 

いくつかの合併症があります。このツリーを考えてみましょう:

<root> 
    <child/> 
    <child/> 
</root> 

2つの子ノードの違いをどのように教えてください。したがって、あなたのアイテムシーケンスには、子供1と子供[2]などのインデックスが必要です。

3

祖先祖先XPath Axesを使用して、親と祖父母をすべて歩くことができます。

<xsl:for-each select="ancestor::*">... 
3

私はあなたが使用しているどのXSLTプロセッサわからないんだけど、それはサクソンされている場合は、拡張機能path()を使用することができます。他のプロセッサは同じ機能を有することができる。

20

現在受け入れられている回答は、不正なパスを返します。たとえば、OPサンプルXMLの要素Ele2は、パス/Doc[1]/Ele2[2]を返します。それは/Doc[1]/Ele2[1]でなければなりません。ここで

が正しいパスを返す同様のXSLT 1.0テンプレートです:

<xsl:template name="genPath"> 
    <xsl:param name="prevPath"/> 
    <xsl:variable name="currPath" select="concat('/',name(),'[', 
     count(preceding-sibling::*[name() = name(current())])+1,']',$prevPath)"/> 
    <xsl:for-each select="parent::*"> 
     <xsl:call-template name="genPath"> 
     <xsl:with-param name="prevPath" select="$currPath"/> 
     </xsl:call-template> 
    </xsl:for-each> 
    <xsl:if test="not(parent::*)"> 
     <xsl:value-of select="$currPath"/>  
    </xsl:if> 
    </xsl:template> 

ここではすべての要素にpath属性を追加します例です。

XML入力

<Doc> 
    <Ele1> 
    <Ele11> 
     <Ele111> 
     <foo/> 
     <foo/> 
     <bar/> 
     <foo/> 
     <foo/> 
     <bar/> 
     <bar/> 
     </Ele111> 
    </Ele11> 
    </Ele1> 
    <Ele2/> 
</Doc> 

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="text()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*"> 
    <xsl:copy> 
     <xsl:attribute name="path"> 
     <xsl:call-template name="genPath"/> 
     </xsl:attribute> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy>  
    </xsl:template> 

    <xsl:template name="genPath"> 
    <xsl:param name="prevPath"/> 
    <xsl:variable name="currPath" select="concat('/',name(),'[', 
     count(preceding-sibling::*[name() = name(current())])+1,']',$prevPath)"/> 
    <xsl:for-each select="parent::*"> 
     <xsl:call-template name="genPath"> 
     <xsl:with-param name="prevPath" select="$currPath"/> 
     </xsl:call-template> 
    </xsl:for-each> 
    <xsl:if test="not(parent::*)"> 
     <xsl:value-of select="$currPath"/>  
    </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 

XML出力

<Doc path="/Doc[1]"> 
    <Ele1 path="/Doc[1]/Ele1[1]"> 
     <Ele11 path="/Doc[1]/Ele1[1]/Ele11[1]"> 
     <Ele111 path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]"> 
      <foo path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/foo[1]"/> 
      <foo path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/foo[2]"/> 
      <bar path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/bar[1]"/> 
      <foo path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/foo[3]"/> 
      <foo path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/foo[4]"/> 
      <bar path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/bar[2]"/> 
      <bar path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/bar[3]"/> 
     </Ele111> 
     </Ele11> 
    </Ele1> 
    <Ele2 path="/Doc[1]/Ele2[1]"/> 
</Doc> 

はここで別のバージョンがありますそれが必要な場合にのみ位置述語を出力します。この例は、属性を追加するのではなくパスを出力するという点でも異なります。XSLTとXmlPrime 4でversion="3.0"とサクソン9.8(すべてのエディション)またはサクソン9.7でサポートされているように

/Doc 
/Doc/Ele1 
/Doc/Ele1/Ele11 
/Doc/Ele1/Ele11/Ele111 
/Doc/Ele1/Ele11/Ele111/foo[1] 
/Doc/Ele1/Ele11/Ele111/foo[2] 
/Doc/Ele1/Ele11/Ele111/bar[1] 
/Doc/Ele1/Ele11/Ele111/foo[3] 
/Doc/Ele1/Ele11/Ele111/foo[4] 
/Doc/Ele1/Ele11/Ele111/bar[2] 
/Doc/Ele1/Ele11/Ele111/bar[3] 
/Doc/Ele2 
+0

+1のXPATH式。 –

1

のXPath 3.0ので(--xt30を使用):上記の入力、このスタイルシートの出力を用いて

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="text()"/> 

    <xsl:template match="*"> 
     <xsl:for-each select="ancestor-or-self::*"> 
      <xsl:value-of select="concat('/',local-name())"/> 
      <!--Predicate is only output when needed.--> 
      <xsl:if test="(preceding-sibling::*|following-sibling::*)[local-name()=local-name(current())]"> 
       <xsl:value-of select="concat('[',count(preceding-sibling::*[local-name()=local-name(current())])+1,']')"/> 
      </xsl:if> 
     </xsl:for-each> 
     <xsl:text>&#xA;</xsl:text> 
     <xsl:apply-templates select="node()"/> 
    </xsl:template> 

</xsl:stylesheet> 

同様のAltovaの2017のリリース(version="3.0"スタイルシートを使用して)として存在する内蔵path関数(https://www.w3.org/TR/xpath-functions-30/#func-pathhttps://www.w3.org/TR/xpath-functions-31/#func-path

<?xml version="1.0" encoding="UTF-8"?> 
<Doc> 
    <Ele1> 
     <Ele11> 
      <Ele111> 
       <foo/> 
       <foo/> 
       <bar/> 
       <foo/> 
       <foo/> 
       <bar/> 
       <bar/> 
      </Ele111> 
     </Ele11> 
    </Ele1> 
    <Ele2/> 
</Doc> 
ような入力のためにどの

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:math="http://www.w3.org/2005/xpath-functions/math" 
    exclude-result-prefixes="xs math" 
    version="3.0"> 

    <xsl:output method="text"/> 

    <xsl:template match="/"> 
     <xsl:value-of select="//*/path()" separator="&#10;"/> 
    </xsl:template> 

</xsl:stylesheet> 

ようなスタイルシートは、出力が最も手作りの試みとして、名前空間の不足の場合のようにコンパクトではないが、形式は、AT(という利点を有する出力

/Q{}Doc[1] 
/Q{}Doc[1]/Q{}Ele1[1] 
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1] 
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1] 
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}foo[1] 
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}foo[2] 
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}bar[1] 
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}foo[3] 
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}foo[4] 
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}bar[2] 
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}bar[3] 
/Q{}Doc[1]/Q{}Ele2[1] 

を与えます最小限のXPath 3.0または3.1サポート)を使用して、名前空間を使用できるようにし、パス式のユーザーに名前空間バインディングを設定して評価する必要のない返されたパスの形式を取得します。

+0

ニース。私はreplace()を使用して、名前空間の内容を取り除きました。これは私の特定のコンテキストでは混乱していました。私は名前空間情報がそこにあることをうれしいです。 – David

関連する問題