Reputation: 899
Given the following XML
<section>
<title>Title1</title>
<item>Item1</item>
<item>Item2</item>
<title>Title2</title>
<item>Item1</item>
<item>Item2</item>
</section>
What is the easiest way to split at second title into this
<section>
<subsection>
<title>Title1</title>
<item>Item1</item>
<item>Item2</item>
</subsection>
<subsection>
<title>Title2</title>
<item>Item1</item>
<item>Item2</item>
</subsection>
</section>
for example the following template was one idea but the xpath seems tricky.
<xsl:template match="section">
<xsl:copy>
<subsection>
<xsl:apply-templates select="...those before second title..."/>
</subsection>
<subsection>
<xsl:apply-templates select="...those including and after second title..."/>
</subsection>
</xsl:copy>
</xsl:template>
Upvotes: 3
Views: 269
Reputation: 122364
With XSLT 2.0 or later it's a straightforward use of for-each-group
:
<xsl:template match="section">
<xsl:copy>
<xsl:for-each-group select="*" group-starting-with="title">
<subsection>
<xsl:apply-templates select="current-group()"/>
</subsection>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
In 1.0 you can achieve a similar effect with this kind of logic:
<xsl:template match="section">
<xsl:copy>
<xsl:for-each select="title">
<subsection>
<xsl:apply-templates select=". | following-sibling::item[
generate-id(preceding-sibling::title[1])
= generate-id(current())]"/>
</subsection>
</xsl:for-each>
</xsl:copy>
</xsl:template>
The predicate finds those following sibling item
elements of the current title
whose nearest preceding title
is the one we started from.
One difference between the two approaches is if you have any other elements in a section
before the first title
, then the for-each-group
approach will put them in a leading subsection (which will have no title), whereas the 1.0 approach will ignore them.
If you always want exactly two subsections (with everything after the second title
in the second subsection, including any further title
elements) then you can just hard-code it as
<xsl:template match="section">
<xsl:copy>
<subsection>
<xsl:apply-templates select="title[2]/preceding-sibling::*"/>
</subsection>
<subsection>
<xsl:apply-templates select="title[2] | title[2]/following-sibling::*"/>
</subsection>
</xsl:copy>
</xsl:template>
(Note that if there are fewer than two title
elements this will result in two completely empty <subsection/>
elements)
Upvotes: 3