Reputation: 21
I need to merge two siblings
<table>
<tbody>
<tr>
<td> table data</td>
</tr>
</tbody>
<tbody>
<tr>
<td> table data</td>
</tr>
</tbody>
</table>
Expected output:
<table>
<tbody>
<tr>
<td> table data</td>
</tr>
<tr><td> table data</td>
</tr>
</tbody>
</table>
My xslt code is :
<xsl:template match="/">
<xsl:for-each-group select="*" group-adjacent="boolean(self::tbody)">
<tbody>
<xsl:value-of select="."/>
</tbody>
</xsl:for-each-group>
</xsl:template>
which is not giving the correct output. Could you please suggest
Upvotes: 0
Views: 256
Reputation: 70618
There are a number of issues with your current XSLT
table
element, not the table
element itself. As you are trying to group on child tbody
elements, your template should match the table
elementxsl:value-of
returns just the text value of a node. You should be using xsl:copy-of
here (or xsl:apply-templates
in conjunction with the identity template). You should also select all items in the group, not just the current item.tbody
So, your XSLT should look like this....
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="table">
<table>
<xsl:for-each-group select="*" group-adjacent="boolean(self::tbody)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<tbody>
<xsl:apply-templates select="current-group()/*"/>
</tbody>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</table>
</xsl:template>
</xsl:stylesheet>
Note, if you are using XSLT 3.0, you can replace the identity template with this:
<xsl:mode on-no-match="shallow-copy"/>
On the other hand, if you are actually only to use XSLT 1.0, then you need to do Muenchian Grouping. That means defining a key like so:
<xsl:key name="table" match="table/*" use="boolean(self::tbody)" />
Then, instead of using xsl:for-each-group
, do this (although this will group all tbody
elements, not just adjacent ones)
<xsl:for-each select="*[generate-id() = generate-id(key('table', boolean(self::tbody))[1])]">
Try this XSLT instead
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="table" match="table/*" use="boolean(self::tbody)" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="table">
<table>
<xsl:for-each select="*[generate-id() = generate-id(key('table', boolean(self::tbody))[1])]">
<xsl:choose>
<xsl:when test="self::tbody">
<tbody>
<xsl:apply-templates select="key('table', true())/*"/>
</tbody>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="key('table', false())" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Of course, after all this, Michael.Hor257k's answer is much simpler in this case. (Although it is definitely worth reading up on Muenchian Grouping if indeed you are stuck with XSLT 1.0).
Upvotes: 1
Reputation: 116993
Your example could be handled simply by:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/table">
<xsl:copy>
<tbody>
<xsl:copy-of select="tbody/*"/>
</tbody>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0