Reputation: 23
I have an XML document which needs some XSLT transformation. Here is the XML:
<docs>
<!-comment->
<doc>
<node add="1"/>
<node add="2"/>
<!-comment->
<node add="3"/>
<node add="4"/>
<node add="5"/>
<!-comment->
<node add="6"/>
<node add="7"/>
<node add="8"/>
</doc>
</docs>
After transformation, the above structure should look like this:
<docs>
<!-comment->
<doc>
<node add="1"/>
<node add="2"/>
</doc>
<!-comment->
<doc>
<node add="3"/>
<node add="4"/>
<node add="5"/>
</doc>
<!-comment->
<doc>
<node add="6"/>
<node add="7"/>
<node add="8"/>
</doc>
</docs>
The code that I have so far is:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@* | node()" mode="#all">
<xsl:copy>
<xsl:apply-templates select="@* , node()" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="docs">
<xsl:for-each select="comment">
<doc>
<xsl:copy-of select="following-sibling::node[not(following-sibling::comment)]"/>
<doc>
<xsl:for-each>
</xsl:template>
This code does copy the nodes but it copies all the nodes which come after the comment(which is wrong). Structurally, I can tell what I am doing wrong but I can't come up with a correct pattern which would group the nodes and place them accordingly. Also, the number of nodes can change for different XML.
Upvotes: 0
Views: 40
Reputation: 1009
The XSLT element xsl:for-each-group
was invented for these scenarios. See https://www.saxonica.com/html/documentation/xsl-elements/for-each-group.html for more information.
My solution:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="doc">
<xsl:for-each-group select="node|comment()" group-starting-with="comment()">
<xsl:copy-of select="current-group()[self::comment()]"/>
<doc>
<xsl:copy-of select="current-group()[self::element()]"/>
</doc>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1