I. Это XSLT 2.0 преобразование :
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:for-each-group select="/*/*"
group-adjacent="boolean(self::com/head)">
<xsl:apply-templates select="current-group()[1]">
<xsl:with-param name="pGroup" select="current-group()"/>
</xsl:apply-templates>
</xsl:for-each-group>
</root>
</xsl:template>
<xsl:template match="com[head]">
<xsl:param name="pGroup"/>
<l>
<xsl:apply-templates select="$pGroup" mode="inGroup"/>
</l>
</xsl:template>
<xsl:template match="com[head]" mode="inGroup">
<li>
<lab><xsl:value-of select="head"/></lab>
<type><xsl:value-of select="body"/></type>
</li>
</xsl:template>
<xsl:template match="*[not(self::com/head)]">
<xsl:param name="pGroup"/>
<xsl:apply-templates select="$pGroup" mode="inGroup"/>
</xsl:template>
<xsl:template match="*[not(self::com/head)]" mode="inGroup">
<p><xsl:value-of select="."/></p>
</xsl:template>
</xsl:stylesheet>
при применении к предоставленному документу XML :
<root>
<com>
<head>A</head>
<body>Type A</body>
</com>
<com>
<head>B</head>
<body>Type B</body>
</com>
<com>
<head>C</head>
<body>Type C</body>
</com>
<com>
<head>D</head>
<body>Type D</body>
</com>
<com>
<body>Type No</body>
</com>
<plom>xml type X</plom>
<plom>xml type Y</plom>
<com>
<head>a</head>
<body>Type a</body>
</com>
<com>
<head>b</head>
<body>Type b</body>
</com>
</root>
дает желаемый, правильный результат :
<root>
<l>
<li>
<lab>A</lab>
<type>Type A</type>
</li>
<li>
<lab>B</lab>
<type>Type B</type>
</li>
<li>
<lab>C</lab>
<type>Type C</type>
</li>
<li>
<lab>D</lab>
<type>Type D</type>
</li>
</l>
<p>Type No</p>
<p>xml type X</p>
<p>xml type Y</p>
<l>
<li>
<lab>a</lab>
<type>Type a</type>
</li>
<li>
<lab>b</lab>
<type>Type b</type>
</li>
</l>
</root>
Объяснение
Использование <xsl:for-each-group group-adjacent="...">
. Каждая из выбранных групп смежных элементов имеет одинаковое значение для boolean(self::com/head)
- либо все true()
, либо все ложные false()
.
Указана только обработка первого элемента в каждой группе, обеспечивающая в качестве параметра последовательность всех элементов в группе.
Шаблон, соответствующий элементу com
, у которого есть дочерний элемент head
(он выбирается для выполнения, только если это первый элемент в группе), упаковывает все результаты обработки элементов группы в элемент l
, затем инициирует обработку отдельных элементов в группе в режиме inGroup
.
Шаблон, соответствующий любому другому (не com
, имеющему head
дочерний) элементу (он выбирается для выполнения только в том случае, если это первый элемент в группе), просто инициирует обработку отдельных элементов. в группе в режиме inGroup
II. XSLT 1.0 решение :
<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="kComHeadGroup" match="com[head]"
use="generate-id(following-sibling::*[not(self::com[head])][1])"/>
<xsl:key name="kNonComHeadGroup" match="*[not(self::com/head)]"
use="generate-id(following-sibling::*[(self::com[head])][1])"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="/*/*[1]"/>
</root>
</xsl:template>
<xsl:template match=
"com[head
and
not(preceding-sibling::*[1]/self::com[head])
]">
<l>
<xsl:apply-templates mode="inGroup" select=
"key('kComHeadGroup',
generate-id
(following-sibling::*
[not(self::com[head])]
[1]
)
)"/>
</l>
<xsl:apply-templates select=
"following-sibling::*
[not(self::com[head])]
[1]
"/>
</xsl:template>
<xsl:template match="com[head]" mode="inGroup">
<li>
<lab><xsl:value-of select="head"/></lab>
<type><xsl:value-of select="body"/></type>
</li>
</xsl:template>
<xsl:template match="*[not(self::com/head)]">
<xsl:apply-templates mode="inGroup" select=
"key('kNonComHeadGroup',
generate-id(following-sibling::*[(self::com[head])][1])
)
"/>
<xsl:apply-templates select=
"following-sibling::com[head][1]"/>
</xsl:template>
<xsl:template match="*[not(self::com/head)]" mode="inGroup">
<p><xsl:value-of select="."/></p>
</xsl:template>
</xsl:stylesheet>
при применении к тому же XML-документу (см. Выше), снова тот же самый требуемый, правильный результат:
<root>
<l>
<li>
<lab>A</lab>
<type>Type A</type>
</li>
<li>
<lab>B</lab>
<type>Type B</type>
</li>
<li>
<lab>C</lab>
<type>Type C</type>
</li>
<li>
<lab>D</lab>
<type>Type D</type>
</li>
</l>
<p>Type No</p>
<p>xml type X</p>
<p>xml type Y</p>
<l>
<li>
<lab>a</lab>
<type>Type a</type>
</li>
<li>
<lab>b</lab>
<type>Type b</type>
</li>
</l>
</root>
Объяснение : точно такая же логика, что и в решении XSLT 2.0, но при реализации группировки используются ключи.