Die aktuell angenommene Antwort gibt falsche Pfade zurück. Beispielsweise würde das Element Ele2
in der OP-Beispiel-XML den Pfad /Doc[1]/Ele2[2]
zurückgeben. Es sollte /Doc[1]/Ele2[1]
sein.
Hier ist eine ähnliche XSLT 1.0-Vorlage, die die richtigen Pfade zurückgibt:
<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>
Hier ist ein Beispiel, das ein path
Attribut für alle Elemente hinzufügen.
XML-Eingabe
<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-Ausgabe
<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>
hier eine andere Version ist Das gibt nur das Positionsprädikat aus, wenn es benötigt wird. Dieses Beispiel unterscheidet sich auch darin, dass nur der Pfad ausgegeben wird, anstatt ein Attribut hinzuzufügen.
<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>
</xsl:text>
<xsl:apply-templates select="node()"/>
</xsl:template>
</xsl:stylesheet>
der Eingang über Verwendung dieses Stylesheet Ausgänge:
/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
+1 für XPATH-Ausdrücke. –