Эта таблица стилей:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kCByFollSep" match="C"
use="generate-id(following::separator[1])"/>
<xsl:template match="A">
<xsl:for-each select="B/separator|B[last()]/*[last()]">
<A>
<xsl:apply-templates
select="key('kCByFollSep',
substring(generate-id(),
1 div boolean(self::separator)))"/>
</A>
<xsl:copy-of select="self::separator"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="C">
<B>
<xsl:copy-of select="."/>
</B>
</xsl:template>
</xsl:stylesheet>
Вывод:
<A>
<B>
<C id="1" />
</B>
</A>
<separator />
<A>
<B>
<C id="2" />
</B>
<B>
<C id="3" />
</B>
</A>
<separator />
<A>
<B>
<C id="4" />
</B>
</A>
Примечание : группировка по следующему separator
, добавление последнего элемента третьего уровня для возможного C
без следования separator
.
Редактировать : эта таблица стилей имеет больше стилей, больше не зависит от схемы:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kCByFollSep" match="C"
use="generate-id(following::separator[1])"/>
<xsl:template match="text()"/>
<xsl:template match="separator|*[not(*)][not(following::*)]">
<A>
<xsl:apply-templates
select="key('kCByFollSep',
substring(generate-id(),
1 div boolean(self::separator)))"
mode="output"/>
</A>
<xsl:copy-of select="self::separator"/>
</xsl:template>
<xsl:template match="C" mode="output">
<B>
<xsl:copy-of select="."/>
</B>
</xsl:template>
</xsl:stylesheet>
ПРАВКА 2 :Более общее решение (одна вещь, которой я не доверяю, да!)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*" name="identity">
<xsl:param name="pRemains"/>
<xsl:copy>
<xsl:apply-templates select="node()[descendant-or-self::node()
[not(self::separator)]
[count(following::separator)
= $pRemains]
][1]|@*">
<xsl:with-param name="pRemains" select="$pRemains"/>
</xsl:apply-templates>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()
[descendant-or-self::node()
[not(self::separator)]
[count(following::separator)
= $pRemains]
][1]">
<xsl:with-param name="pRemains" select="$pRemains"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="/*">
<xsl:variable name="vCurrent" select="."/>
<xsl:for-each select="descendant::separator|node()[last()]">
<xsl:variable name="vRemains" select="last()-position()"/>
<xsl:for-each select="$vCurrent">
<xsl:copy>
<xsl:apply-templates
select="node()[descendant::node()
[not(self::separator)]
[count(following::separator)
= $vRemains]
][1]">
<xsl:with-param name="pRemains" select="$vRemains"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:for-each>
<xsl:copy-of select="self::separator"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="separator"/>
</xsl:stylesheet>
Примечание : В основном, мелкозернистый обход.Правило иерархии этажей (в данном случае корневой элемент) копирует себя и разделитель (фиктивный узел для последней группы без разделителя), пропуская разделители остатков для обработки первого потомка с достаточным количеством следующих разделителей для обработки.Модифицированное правило идентификации мелкозернистого обхода, копирующее себя и снова обрабатывающее первого потомка и следующего родного брата с достаточным количеством следующих разделителей для обработки.Наконец, правило разделителя, нарушающее процесс.
Редактировать 3 : Другое более общее решение, теперь с рекурсивным правилом идентификации
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="kNodeByFolSep" match="node()[not(self::separator)]"
use="generate-id((descendant::separator|following::separator)[1])"/>
<xsl:template match="node()|@*" name="identity">
<xsl:param name="pGroup"/>
<xsl:copy>
<xsl:apply-templates
select="node()[descendant-or-self::node()[count(.|$pGroup)
= count($pGroup)]]|@*">
<xsl:with-param name="pGroup" select="$pGroup"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:variable name="vCurrent" select="."/>
<xsl:for-each select="descendant::separator|node()[last()]">
<xsl:variable name="vGroup"
select="key('kNodeByFolSep',generate-id(self::separator))"/>
<xsl:for-each select="$vCurrent">
<xsl:call-template name="identity">
<xsl:with-param name="pGroup" select="$vGroup"/>
</xsl:call-template>
</xsl:for-each>
<xsl:copy-of select="self::separator"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="separator"/>
</xsl:stylesheet>
Редактировать 4 : Теперь точно так же, как и раньше, но с проверкой ключа вместо пересечения набора узлов.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="kNodeByFolSep" match="node()[not(self::separator)]"
use="concat(generate-id(),'+',
generate-id((descendant::separator|
following::separator)[1]))"/>
<xsl:template match="node()|@*" name="identity">
<xsl:param name="pSeparator"/>
<xsl:copy>
<xsl:apply-templates
select="@*|node()[descendant-or-self::node()
[key('kNodeByFolSep',
concat(generate-id(),
'+',
$pSeparator))]]">
<xsl:with-param name="pSeparator" select="$pSeparator"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:variable name="vCurrent" select="."/>
<xsl:for-each select="descendant::separator|node()[last()]">
<xsl:variable name="vSeparator"
select="generate-id(self::separator)"/>
<xsl:for-each select="$vCurrent">
<xsl:call-template name="identity">
<xsl:with-param name="pSeparator" select="$vSeparator"/>
</xsl:call-template>
</xsl:for-each>
<xsl:copy-of select="self::separator"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="separator"/>
</xsl:stylesheet>