This is a pretty straightforward grouping problem.
If you’re limited to XSLT 1.0, you need to use Muenchian Grouping.
If you’re using XSLT 2.0+, you can use xsl:for-each-group.
Examples…
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="class" match="text"
use="substring-before(substring-after(normalize-space(@class), ' '),' ')"/>
<!--identity template-->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/name">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each select="text[count(.|key('class', substring-before(substring-after(normalize-space(@class), ' '),' '))[1])=1]">
<xsl:variable name="key" select="substring-before(substring-after(normalize-space(@class), ' '),' ')"/>
<group name="{$key}">
<xsl:apply-templates select="key('class',$key)"/>
</group>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Fiddle: http://xsltfiddle.liberty-development.net/94hvTyR
XSLT 3.0 (you can make this valid 2.0 if you replace the xsl:mode
with the identity template from the 1.0 stylesheet)
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="name">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="text"
group-by="tokenize(normalize-space(@class),'\s+')[2]">
<group name="{current-grouping-key()}">
<xsl:apply-templates select="current-group()"/>
</group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Fiddle: http://xsltfiddle.liberty-development.net/94hvTyR/1
Note: The output does not have the same order as your example, but I didn’t see any logic to the ordering.
5
solved Group Similar nodes in XML using XLST