I'm new to XSLT and I'm stuck on the "grouping adjacent elements" problem. My XML input follows the schema:
<body>
<aside >
some tags text
</aside>
<aside >
some tags text
</aside>
<aside >
some tags text
</aside>
<p>...</p>
<img .../>
<aside >
some tags text
</aside>
<aside >
some tags text
</aside>
</body>
What I need (and can't achieve) is an output in which the adjacent aside
with the same class are grouped together. The desidered output would be like this:
<body>
<tag1 >everything contained in the grouped tags</tag1>
<tag2 >everything contained in the grouped tags</tag2>
<p>...</p>
<img.../>
<tag2 >everything contained in the grouped tags</tag2>
</body>
The new tags can be managed using variables, and that's fine. But how do I obtain this output? So far, I've obtained the best results with this:
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="body">
<xsl:for-each-group select="aside" group-adjacent="@class">
<xsl:variable name="grouping_tag">
<xsl:choose>
<xsl:when test="@class = '1'">tag1</xsl:when>
<xsl:when test="@class = '2'">tag2</xsl:when>
<xsl:otherwise>tagX</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{$grouping_tag}" >
<xsl:attribute name="class"><xsl:value-of select="@class"/></xsl:attribute>
<xsl:for-each select="current-group()/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</xsl:template>
This solution does indeed group adjacent aside
with the same class, but in the output I lost everything that is not contained in them (for example, p
, img
). And I need to mantain everything else, and in the same position.
Can someone help me?
CodePudding user response:
I would use select="*" group-adjacent=". instance of element(aside), @class" composite="yes"
and then you can test current-group()[1]
to check the group has aside
elements and wrap them, otherwise push the group through apply-templates.
CodePudding user response:
Something like this, perhaps?
<xsl:template match="body">
<xsl:for-each-group select="aside" group-adjacent="@class">
<xsl:choose>
<xsl:when test="@class">
<xsl:element name="tag{@class}">
<xsl:copy-of select="current-group()"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
CodePudding user response:
I guess you could do something like:
<xsl:template match="body">
<xsl:copy>
<xsl:for-each-group select="*" group-adjacent="boolean(self::aside)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:for-each-group select="current-group()" group-adjacent="@class">
<xsl:element name="tag{if (@class=(1,2)) then @class else 'X'}" >
<xsl:attribute name="class" select="@class"/>
<xsl:copy-of select="current-group()/node()"/>
</xsl:element>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>