Это почти то же решение, что и у DevNull, но в случае конфликта между существующим атрибутом и новым, определенным дочерним элементом, последний заменяет первый :
<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:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates mode="attr" select="*[starts-with(name(), 'attrib-')]"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template mode="attr" match="*[starts-with(name(), 'attrib-')]">
<xsl:attribute name="{substring-after(name(), 'attrib-')}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="*[starts-with(name(), 'attrib-')]"/>
</xsl:stylesheet>
когда это преобразование применяется к следующему документу XML :
<a1>
<b1 existing="attr" x="Y">
<attrib-new>12</attrib-new>
<c2>23</c2>
<attrib-new2>ABCD</attrib-new2>
<attrib-x>Z</attrib-x>
</b1>
</a1>
желаемый, правильный результат получается :
<a1>
<b1 existing="attr" x="Z" new="12" new2="ABCD">
<c2>23</c2>
</b1>
</a1>