Suppose I have the following XML:
<?xml version="1.0" encoding="utf8"?>
<test>
<list>
<li>a</li>
<li>a</li>
<li begin="true">b</li> <!-- begin of the "b" list -->
<li>b</li>
<li>b</li>
<li end="true">b</li> <!-- end of the "b" list -->
<li>c</li>
<li>c</li>
</list>
</test>
and I wanted to split this list into three lists based on the begin
and end
attributes using XSLT 1.0, such that the result is:
<list>
<li>a</li>
<li>a</li>
</list>
<list>
<li>b</li>
<li>b</li>
<li>b</li>
<li>b</li>
</list>
<list>
<li>c</li>
<li>c</li>
</list>
I managed to select the first and third list but struggle with matching the range of the middle list:
<xsl:template match="list">
<list>
<xsl:apply-templates select="li[@begin = 'true']/preceding-sibling::li" />
</list>
<list>
<xsl:apply-templates select="li[...]" /> <!-- Hmm 🤔 -->
</list>
<list>
<xsl:apply-templates select="li[@end = 'true']/following-sibling::li" />
</list>
</xsl:template>
I tried various expressions using position()
and count()
but can't quite get this right.
Addendum: the attributes begin
and end
are unique, i.e. there is exactly one such group in the original list.
In the given example, with only 1 marked group (so only 3 groups in total), you could use:
<list>
<xsl:apply-templates select="li[@begin='true']" />
<xsl:apply-templates select="li[@begin='true']/following-sibling::li[not(preceding-sibling::li[@end='true'])]"/>
</list>
(I have split this into 2 separate instructions for easier reading.)
If you are using the libxslt processor, you can take advantage of some extension functions that it supports and do:
<xsl:template match="list">
<xsl:variable name="head" select="set:leading(li, li[@begin = 'true'])"/>
<xsl:variable name="tail" select="set:trailing(li, li[@end = 'true'])"/>
<list>
<xsl:apply-templates select="$head"/>
</list>
<list>
<xsl:apply-templates select="set:difference(li, $head | $tail)"/>
</list>
<list>
<xsl:apply-templates select="$tail"/>
</list>
</xsl:template>
provided you declare:
xmlns:set="http://exslt.org/sets"
extension-element-prefixes="set"
in the xsl:stylesheet
start-tag.