Вот решение с XSLT 1.0. Я прокомментировал как можно больше. Я использовал решение для прогулки по дереву. Прогулка с деревьями - не самая простая вещь (для начинающих) в XSLT. Для общего объяснения этого подхода смотрите здесь: Dave Pawson XSLT FAQ
Внутри узла <root>
XSLT шаг за шагом проходит / проходит по прямым потомкам <root>
и генерирует новый блок <main>...</main>
(на каждом <main>
узле) или <children>...</children>
(<child>
) который имеет прямой <main>
родитель) на некоторых узлах.
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:copy>
<!-- copy any attributes in root -->
<xsl:apply-templates select="@*"/>
<!-- walk through and copy nodes which are preceding main (if there are any?) -->
<xsl:apply-templates select="*[1][not(self::main)]" mode="walker"/>
<!-- start walking mode with first main -->
<xsl:apply-templates select="*[self::main][1]" mode="walker"/>
</xsl:copy>
</xsl:template>
<!-- do not copy main, it will be copied later in walker mode -->
<xsl:template match="root/main"/>
<xsl:template match="main" mode="walker">
<main>
<!-- copy any attributes of main -->
<xsl:apply-templates select="@*"/>
<!-- copy all children of main -->
<xsl:apply-templates/>
<!-- begin walking and copying the next following-sibling node IF it is not main -->
<xsl:apply-templates select="following-sibling::*[1][not(self::main)]" mode="walker"/>
</main>
<!-- search the next main start and generate a new main block -->
<xsl:apply-templates select="following-sibling::*[self::main][1]" mode="walker"/>
</xsl:template>
<!-- every child which has main as it first preceding-sibling generates a new children block -->
<xsl:template match="child[preceding-sibling::*[1][self::main]]" mode="walker">
<children>
<xsl:apply-templates select="."/>
<!-- continue walking (through children) if the following-sibling node is NOT main -->
<xsl:apply-templates select="following-sibling::*[1][not(self::main)]" mode="walker"/>
</children>
</xsl:template>
<!-- copy all other nodes in walking mode -->
<xsl:template match="*" mode="walker">
<!-- copy this node and children -->
<xsl:apply-templates select="."/>
<!-- walk to the next following-sibling IF it is not main -->
<xsl:apply-templates select="following-sibling::*[1][not(self::main)]" mode="walker"/>
</xsl:template>
<!-- Default: Copy everything -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
производит желаемый результат:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<main>
<id>100</id>
<children>
<child>1</child>
<child>2</child>
</children>
</main>
<main>
<id>200</id>
<children>
<child>1</child>
<child>2</child>
<child>3</child>
</children>
</main>
<main>
<id>300</id>
<children>
<child>1</child>
</children>
</main>
</root>