0
votes

I have xml like follows,

        <table>
            <tbody>
                <tr>
                    <th>Test table head col 1</th>
                    <th><span>Test table head col 2</span></th>
                    <th><span>Test table head col 3</span></th>
                </tr>
                <tr>
                    <td>Row 1 Column 1</td>
                    <td>Row 1 Column 2</td>
                    <td>Row 1 Column 3</td></tr>
                <tr>
                    <td>Row 2 Column 1</td>
                    <td>Row 2 Column 2</td>
                    <td>Row 2 Column 3</td>
                </tr>
                <tr>
                    <td>Row 3 Column 1</td>
                    <td>Row 3 Column 2</td>
                    <td>Row 3 Column 4</td>
                </tr>
            </tbody>
        </table>

using XSLT I have to get the following output from above xml,

<table>       
                <thead>
                    <row>
                        <entry namest="1">
                            <p type="Table head">Test table head col 1</p>
                        </entry>
                        <entry namest="2">
                            <p type="Table head">Test table head col 2</p>
                        </entry>
                        <entry namest="3">
                            <p type="Table head">Test table head col 3</p>
                        </entry>
                    </row>
                </thead>
                <tbody>
                    <row>
                        <entry namest="1" align="left">
                            <p type="Table text">Row 1 Column 1</p>
                        </entry>
                        <entry namest="2" align="left">
                            <p type="Table text">Row 1 Column 2</p>
                        </entry>
                        <entry namest="3" align="left">
                            <p type="Table text">Row 1 Column 3</p>
                        </entry>
                    </row>
                    <row>
                        <entry namest="1" align="left">
                            <p type="Table text">Row 2 Column 1</p>
                        </entry>
                        <entry namest="2" align="left">
                            <p type="Table text">Row 2 Column 2</p>
                        </entry>
                        <entry namest="3" align="left">
                            <p type="Table text">Row 2 Column 3</p>
                        </entry>
                    </row>
                    <row>
                        <entry namest="1" align="left">
                            <p type="Table text">Row 3 Column 1</p>
                        </entry>
                        <entry namest="2" align="left">
                            <p type="Table text">Row 3 Column 2</p>
                        </entry>
                        <entry namest="3" align="left">
                            <p type="Table text">Row 3 Column 4</p>
                        </entry>
                    </row>
                </tbody>                
        </table>

to get that output I've written following xsl,

<xsl:template match="tbody/tr[1]">
        <thead>
            <row>
                <xsl:for-each select="th">
                    <entry namest="{position()}">
                        <p type="Table head"><xsl:apply-templates/></p>
                    </entry>
                </xsl:for-each>
            </row>
        </thead>
    </xsl:template>

    <xsl:template match="tbody/tr[not(position()=1)]">
        <tbody>
            <row>
                <xsl:for-each select="td">
                    <entry namest="{position()}" align="left">
                        <p type="Table text"><xsl:apply-templates/></p>
                    </entry>
                </xsl:for-each>
            </row>
        </tbody>
    </xsl:template>

But it adds <tbody> to every <tr> nodes. How can I modify my code to add only one <tbody> element to cover <tr> elements from second element to final element as my expected output ?

2

2 Answers

1
votes

One approach to take is to match the tbody element, and then do specific processing to create a thead element for the first row, and then only copy the remaining rows to the tbody

Try this XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" />

    <xsl:template match="tbody">
        <thead>
          <xsl:apply-templates select="tr[1]" />
        </thead>
        <tbody>
            <xsl:apply-templates select="tr[position() > 1]" />
        </tbody>
    </xsl:template>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

If you wanted to cope with a situation where there was already a thead, or perhaps no tbody at all, you could match on the table element instead. Try this XSLT too:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" />

    <xsl:template match="table">
      <table>
        <thead>
          <xsl:apply-templates select="(.//tr)[1]" />
        </thead>
        <tbody>
          <xsl:apply-templates select="(.//tr)[position() > 1]" />
        </tbody>
      </table>
    </xsl:template>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
1
votes

This is one possible way :

<xsl:template match="tbody/tr[position()=2]">
    <tbody>
        <xsl:apply-templates/>
    </tbody>
</xsl:template>

<xsl:template match="tbody/tr[position()&gt;1]">
    <row>
        <xsl:for-each select="td">
            <entry namest="{position()}" align="left">
                <p type="Table text"><xsl:apply-templates/></p>
            </entry>
        </xsl:for-each>
    </row>
</xsl:template>

xsltransform.net demo

The first template adds tbody parent element on processing each of the 2nd tr, and the last template adds row element inside the formerly added tbody for each tr at position index greater than 1.