Это сложная проблема. Ваша попытка не удалась, потому что таблица стилей XSLT также должна быть правильно сформированным документом XML
Следующая таблица стилей будет работать на данном примере. Надеемся, что все ваши входные документы будут соответствовать сделанным здесь предположениям.
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="node-by-level" match="node()" use="generate-id(preceding-sibling::LEVEL[.='BEGIN'][1])" />
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/Data">
<xsl:copy>
<xsl:apply-templates select="LEVEL[.='BEGIN'][1]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="LEVEL[.='BEGIN']">
<LEVEL>
<xsl:apply-templates select="key('node-by-level', generate-id())"/>
</LEVEL>
</xsl:template>
<xsl:template match="LEVEL[.='END']"/>
</xsl:stylesheet>
Добавлено:
Чтобы справиться с дополнительной сложностью в вашем отредактированном вопросе, я бы сделал преобразование в два этапа:
XSLT 1.0 (+ функция набора узлов EXSLT)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="elem-by-level" match="*" use="generate-id(preceding-sibling::BEGIN[@level=current()/@level - 1][1])" />
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/Data">
<!-- first pass, using sibling recursion -->
<xsl:variable name="first-pass">
<xsl:apply-templates select="*[1]" mode="first-pass"/>
</xsl:variable>
<!-- output -->
<xsl:copy>
<xsl:apply-templates select="exsl:node-set($first-pass)/*[@level=0]" />
</xsl:copy>
</xsl:template>
<!-- first pass templates -->
<xsl:template match="*" mode="first-pass">
<xsl:param name="level" select="0"/>
<xsl:copy>
<xsl:attribute name="level">
<xsl:value-of select="$level"/>
</xsl:attribute>
<xsl:copy-of select="@*|node()"/>
</xsl:copy>
<xsl:apply-templates select="following-sibling::*[1]" mode="first-pass">
<xsl:with-param name="level" select="$level"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="LEVEL[.='BEGIN']" mode="first-pass">
<xsl:param name="level" select="0"/>
<BEGIN level="{$level}"/>
<xsl:apply-templates select="following-sibling::*[1]" mode="first-pass">
<xsl:with-param name="level" select="$level + 1"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="LEVEL[.='END']" mode="first-pass">
<xsl:param name="level" select="0"/>
<xsl:apply-templates select="following-sibling::*[1]" mode="first-pass">
<xsl:with-param name="level" select="$level - 1"/>
</xsl:apply-templates>
</xsl:template>
<!-- output templates -->
<xsl:template match="BEGIN">
<LEVEL>
<xsl:apply-templates select="key('elem-by-level', generate-id())"/>
</LEVEL>
</xsl:template>
<xsl:template match="@level"/>
</xsl:stylesheet>
Тестирование на следующем примере ввода:
XML
<Data>
<Item name="0A"/>
<Item name="0B"/>
<LEVEL>BEGIN</LEVEL>
<Item name="1A"/>
<LEVEL>BEGIN</LEVEL>
<LEVEL>BEGIN</LEVEL>
<Item name="3A"/>
<LEVEL>END</LEVEL>
<Item name="2A"/>
<LEVEL>END</LEVEL>
<Item name="1B"/>
<LEVEL>END</LEVEL>
<Item name="0C"/>
<LEVEL>BEGIN</LEVEL>
<Item name="1C"/>
<LEVEL>END</LEVEL>
<Item name="0D"/>
</Data>
производит:
Результат
<?xml version="1.0" encoding="utf-16"?>
<Data>
<Item name="0A" />
<Item name="0B" />
<LEVEL>
<Item name="1A" />
<LEVEL>
<LEVEL>
<Item name="3A" />
</LEVEL>
<Item name="2A" />
</LEVEL>
<Item name="1B" />
</LEVEL>
<Item name="0C" />
<LEVEL>
<Item name="1C" />
</LEVEL>
<Item name="0D" />
</Data>
Демоверсия : https://xsltfiddle.liberty -development.net / 3NJ38Zr