La respuesta aceptada actualmente devolverá rutas incorrectas. Por ejemplo, el elemento Ele2
en el XML de ejemplo OP devolvería la ruta /Doc[1]/Ele2[2]
. Debe ser /Doc[1]/Ele2[1]
.
Aquí hay una plantilla similares XSLT 1.0 que devuelve las rutas correctas:
<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>
He aquí un ejemplo que añadir un atributo path
a todos los elementos.
de entrada 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>
salida 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>
Aquí está otra versión que solo emite el predicado posicional si es necesario. Este ejemplo también es diferente, ya que solo está generando la ruta en lugar de agregar un atributo.
<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>
utilizando la entrada anterior, esta salidas de hojas de estilo:
/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 para expresiones XPATH. –