Эта таблица стилей:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kComponentByName" match="component" use="@name"/>
<xsl:template match="test">
<output>
<xsl:call-template name="merge">
<xsl:with-param name="pSequence" select="*"/>
</xsl:call-template>
</output>
</xsl:template>
<xsl:template name="merge">
<xsl:param name="pSequence" select="/.."/>
<xsl:if test="$pSequence">
<xsl:variable name="vName" select="$pSequence[1]/@name"/>
<xsl:for-each select="$pSequence[1]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:call-template name="merge">
<xsl:with-param name="pSequence"
select="key('kComponentByName',$vName)
/component[@name != $vName]"/>
</xsl:call-template>
</xsl:copy>
</xsl:for-each>
<xsl:call-template name="merge">
<xsl:with-param name="pSequence"
select="$pSequence[@name != $vName]"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Выход:
<output>
<component name="root">
<component name="c2">
<component name="c3">
<component name="c4" />
</component>
<component name="A4" />
</component>
<component name="A3" />
<component name="X1">
<component name="X2" />
</component>
</component>
<component name="difRoot" />
</output>
Примечание : рекурсивная иерархия не полностью решена, только предотвращает себя как ребенка.
Обновление : объяснение именованного шаблона: если pSequence
не пустой, возьмите первый узел, затем скопируйте себя и примените шаблоны к дочерним элементам всех элементов component
с одинаковыми @name
( отфильтровывать тех с этим @name
тоже); наконец, вызовите себе фильтрацию pSequence
элементов component
с тем же @name
, что и обработанным. Таким образом, он идет уровень за уровнем и узел за узлом, фильтруя братьев и сестер и детей. Полное предотвращение цикличности должно быть сделано, передавая последовательность с именами предков для фильтрации. Это оставлено как упражнение ...