Это преобразование:
<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="kFollowing" match="node()[not(self::h2)]"
use="generate-id(preceding-sibling::h2[1])"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="h2">
<xsl:call-template name="identity"/>
<div>
<xsl:apply-templates mode="copy"
select="key('kFollowing', generate-id())"/>
</div>
</xsl:template>
<xsl:template match="node()[not(self::h2)][preceding-sibling::h2]"/>
<xsl:template match="node()" mode="copy">
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>
при применении к этому документу XML:
<html>
<h2>Nth title</h2>
<first-child>...</first-child> ...
<last-child>...</last-child>
<h2>N+1st title</h2> ...
<x/>
<y/>
<z/>
</html>
дает желаемый, правильный результат:
<html>
<h2>Nth title</h2>
<div>
<first-child>...</first-child> ...
<last-child>...</last-child>
</div>
<h2>N+1st title</h2>
<div> ...
<x></x>
<y></y>
<z></z>
</div>
</html>
Объяснение
Правило / шаблон идентификации копирует каждый узел "как есть".
Правило идентификации переопределено для h2
элементов. Здесь действие состоит в том, чтобы скопировать элемент h2
, а затем вывести div
и внутри него применить шаблоны (в специальном режиме) ко всем узлам (которые сами не h2
), для которых этот элемент h2
первый предшествующий элемент h2
.
Узлы, которые нужно включить в предыдущий шаг, удобно определить как инструкцию <xsl:key>
.
Для того, чтобы узлы, включенные в div
, снова выводились правилом идентификации, мы предоставляем шаблон, соответствующий таким узлам, который просто игнорирует их.