xsl to group nodes between other nodes?
I can't figure out how create xsl to group some nodes between other nodes. Basically, everytime I see a 'SPLIT' I have to end the di开发者_如何学运维v and create a new one.
The xml looks like this:
<data name="a" />
<data name="b" />
<data name="c" />
<data name="SPLIT" />
<data name="d" />
<data name="e" />
<data name="SPLIT" />
<data name="f" />
<data name="g" />
<data name="h" />
The output needs to look like this
<div>
a
b
c
</div>
<div>
d
e
</div>
<div>
f
g
h
</div>
I know how to do this by 'cheating', but would like to know if there is a proper way to do it:
<div>
<xsl:for-each select="data">
<xsl:choose>
<xsl:when test="@name='SPLIT'">
<xsl:text disable-output-escaping="yes"> </div> <div></xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@name"/>
</xsl:otherwise>
</xsl:for-each>
</div>
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:apply-templates
select="node()[1]|following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="data">
<div>
<xsl:call-template name="open"/>
</div>
<xsl:apply-templates
select="following-sibling::data[@name='SPLIT'][1]
/following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="data" mode="open" name="open">
<xsl:value-of select="concat(@name,'
')"/>
<xsl:apply-templates select="following-sibling::node()[1]"
mode="open"/>
</xsl:template>
<xsl:template match="data[@name='SPLIT']" mode="open"/>
</xsl:stylesheet>
Output:
<div>
a
b
c
</div>
<div>
d
e
</div>
<div>
f
g
h
</div>
Note: Fine grained traversal.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kFollowing" match="data[not(@name='SPLIT')]"
use="generate-id(preceding-sibling::data[@name='SPLIT'][1])"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="data[@name='SPLIT']" name="regularSplit">
<div>
<xsl:apply-templates mode="copy"
select="key('kFollowing', generate-id())"/>
</div>
</xsl:template>
<xsl:template match="data[@name='SPLIT'][
not(preceding-sibling::data[@name='SPLIT'])]">
<div>
<xsl:apply-templates mode="copy"
select="preceding-sibling::data"/>
</div>
<xsl:call-template name="regularSplit"/>
</xsl:template>
<xsl:template match="*" mode="copy">
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match="data[not(@name='SPLIT')]"/>
</xsl:stylesheet>
when applied on the provided XML document (wrapped into a top element to become well-formed):
<t>
<data name="a" />
<data name="b" />
<data name="c" />
<data name="SPLIT" />
<data name="d" />
<data name="e" />
<data name="SPLIT" />
<data name="f" />
<data name="g" />
<data name="h" />
</t>
produces the wanted, correct result:
<t>
<div>
<data name="a"></data>
<data name="b"></data>
<data name="c"></data>
</div>
<div>
<data name="d"></data>
<data name="e"></data>
</div>
<div>
<data name="f"></data>
<data name="g"></data>
<data name="h"></data>
</div>
</t>
Do note: Keys are used to specify conveniently and verry efficiently all "non-SPLIT" elements that follow immediately a "SPLIT" element.
精彩评论