Просто для удовольствия таблица стилей XSLT 1.0:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<xsl:param name="pNames" select="'|'"/>
<xsl:if test="not(contains($pNames,concat('|',name(),'|')))">
<xsl:variable name="vNext" select="following-sibling::*[1]"/>
<xsl:copy>
<xsl:apply-templates select="node()[1]"/>
<xsl:apply-templates select="$vNext">
<xsl:with-param name="pNames"
select="concat($pNames,name(),'|')"/>
</xsl:apply-templates>
</xsl:copy>
<xsl:apply-templates select="$vNext" mode="search">
<xsl:with-param name="pNames" select="$pNames"/>
<xsl:with-param name="pSearch" select="name()"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
<xsl:template match="*" mode="search">
<xsl:param name="pNames"/>
<xsl:param name="pSearch"/>
<xsl:if test="not(contains($pNames,concat('|',name(),'|')))">
<xsl:choose>
<xsl:when test="name()=$pSearch">
<xsl:apply-templates select=".">
<xsl:with-param name="pNames" select="$pNames"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="following-sibling::*[1]"
mode="search">
<xsl:with-param name="pNames" select="$pNames"/>
<xsl:with-param name="pSearch" select="$pSearch"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
С подтвержденным вводом:
<root>
<Header>Header1</Header>
<Sub1>Sub1 first</Sub1>
<Sub1>Sub1 second</Sub1>
<Sub2>Sub2 first, Sub1 second</Sub2>
<Sub1>Sub1 third</Sub1>
<Sub2>Sub2 first, Sub1 third</Sub2>
<Header>Header2</Header>
</root>
Вывод:
<root>
<Header>Header1
<Sub1>Sub1 first</Sub1>
<Sub1>Sub1 second
<Sub2>Sub2 first, Sub1 second</Sub2>
</Sub1>
<Sub1>Sub1 third
<Sub2>Sub2 first, Sub1 third</Sub2>
</Sub1>
</Header>
<Header>Header2</Header>
</root>
С заменой имени иновый реквизит одного уровня с несколькими именами, эта таблица стилей:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:r="rules"
exclude-result-prefixes="r">
<r:r m="|Header|">Header</r:r>
<r:r m="|Sub1|">Step</r:r>
<r:r m="|Sub2|">Step</r:r>
<xsl:variable name="vRules" select="document('')/*/r:r"/>
<xsl:template match="*">
<xsl:param name="pNames" select="'|'"/>
<xsl:variable name="vName" select="concat('|',name(),'|')"/>
<xsl:if test="not(contains($pNames,$vName))">
<xsl:variable name="vNext" select="following-sibling::*[1]"/>
<xsl:variable name="vMatch"
select="$vRules[contains(@m,$vName)]"/>
<xsl:variable name="vReplace"
select="concat($vMatch,name((.)[not($vMatch)]))"/>
<xsl:variable name="vSearch"
select="concat($vMatch/@m,$vName)"/>
<xsl:element name="{$vReplace}">
<xsl:apply-templates select="node()[1]"/>
<xsl:apply-templates select="$vNext">
<xsl:with-param name="pNames"
select="concat($pNames,$vSearch)"/>
</xsl:apply-templates>
</xsl:element>
<xsl:apply-templates select="$vNext" mode="search">
<xsl:with-param name="pNames" select="$pNames"/>
<xsl:with-param name="pSearch" select="$vSearch"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
<xsl:template match="*" mode="search">
<xsl:param name="pNames"/>
<xsl:param name="pSearch"/>
<xsl:variable name="vName" select="concat('|',name(),'|')"/>
<xsl:choose>
<xsl:when test="contains($pNames,$vName)"/>
<xsl:when test="contains($pSearch,$vName)">
<xsl:apply-templates select=".">
<xsl:with-param name="pNames" select="$pNames"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="following-sibling::*[1]"
mode="search">
<xsl:with-param name="pNames" select="$pNames"/>
<xsl:with-param name="pSearch" select="$pSearch"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Вывод:
<root>
<Header>Header1
<Step>Sub1 first</Step>
<Step>Sub1 second
<Step>Sub2 first, Sub1 second</Step>
</Step>
<Step>Sub1 third
<Step>Sub2 first, Sub1 third</Step>
</Step>
</Header>
<Header>Header2</Header>
</root>
Примечание : синонимы для того же уровня должны быть в r:r/@m