Это преобразование :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="Lower" select=
"'abcdefghijklmnopqrstuvwxyz'"
/>
<xsl:variable name="vUpper" select=
"'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"
/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<root>
<xsl:apply-templates select="node()"/>
<xsl:apply-templates select="/*/*//*" mode="extract">
<xsl:sort select="count(ancestor::*)" data-type="number"/>
</xsl:apply-templates>
</root>
</xsl:template>
<xsl:template match="*[ancestor::*[2]]">
<xsl:variable name="vPos">
<xsl:number level="any"/>
</xsl:variable>
<xsl:element name="{name()}">
<xsl:attribute name="ref">
<xsl:value-of select=
"concat(translate(name(),$vUpper,$Lower),$vPos)"/>
</xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template match="*" mode="extract">
<xsl:variable name="vPos">
<xsl:number level="any"/>
</xsl:variable>
<xsl:element name="{name()}">
<xsl:attribute name="id">
<xsl:value-of select=
"concat(translate(name(),$vUpper,$Lower),$vPos)"/>
</xsl:attribute>
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
при применении к предоставленному документу XML :
<root>
<A>
<B att="val">
<C>
someData
</C>
</B>
</A>
<A>
<B>
someOtherData
</B>
<B>
moreData
</B>
</A>
</root>
производит точно требуемый, правильный результат :
<root>
<A>
<B ref="b1"/>
</A>
<A>
<B ref="b2"/>
<B ref="b3"/>
</A>
<B id="b1" att="val">
<C ref="c1"/>
</B>
<B id="b2">
someOtherData
</B>
<B id="b3">
moreData
</B>
<C id="c1">
someData
</C>
</root>
Обратное преобразование :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kElbyId" match="*" use="@id"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@ref]">
<xsl:apply-templates mode="deepen"
select="key('kElbyId',@ref)"/>
</xsl:template>
<xsl:template match="*[@id]"/>
<xsl:template match="*[@id]" mode="deepen">
<xsl:copy>
<xsl:apply-templates
select="@*[not(name()='id')] | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
когда при этом обратное преобразование применяется к результату приведенного выше преобразования сглаживания, создается исходный XML-документ :
<root>
<A>
<B att="val">
<C>
someData
</C>
</B>
</A>
<A>
<B>
someOtherData
</B>
<B>
moreData
</B>
</A>
</root>