Search code examples
htmlxmlxslttei

XSLT dynamic table creation


My goal is to transform an xml file to an html file, and style its contents as a table. However, I only want exactly four elements per row, and I can't seem to find a wayon how to achieve this.

members.xml:

<?xml version = "1.0"?> 
<?xml-stylesheet type = "text/xsl" href = "/teammembers_imports.xsl"?> 
<team>
    <heading>TEST TEAM</heading>
    <member>
        <name>Max Mustermann</name>
        <role>Product Owner</role>
    </member>
    <member>
        <name>Max Mustermann 2</name>
        <role>Product Owner</role>
    </member>
    <member>
        <name>Max Mustermann 3</name>
        <role>Product Owner</role>
    </member>
    <member>
        <name>Max Mustermann 4</name>
        <role>Product Owner</role>
    </member>
    <member>
        <name>Max Mustermann 5</name>
        <role>Product Owner</role>
    </member>
</team>

teammembers_imports.xsl:

<?xml version = "1.0" encoding="UTF-8"?>
<xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"> 

    <xsl:template match = "/">
      <html>
        <body>
          <table>
            <xsl:apply-templates/>
          </table>
        </body>
      </html>
    </xsl:template>
    
    <xsl:template match="member">
      <td>
        <xsl:value-of select="name"/>
      </td>
    </xsl:template>
  </xsl:stylesheet>

Solution

  • Assuming you want a result that looks like: enter image description here

    you could do:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:template match="/team">
        <html>
            <body>
                <table border="1">
                    <xsl:for-each select="member[position() mod 4 = 1]">
                        <tr>
                            <td>
                                <xsl:value-of select="name"/>
                            </td>
                            <td>
                                <xsl:value-of select="following-sibling::member[1]/name"/>
                            </td>
                            <td>
                                <xsl:value-of select="following-sibling::member[2]/name"/>
                            </td>
                            <td>
                                <xsl:value-of select="following-sibling::member[3]/name"/>
                            </td>
                        </tr>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Alternatively you could do:

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:template match="/team">
        <html>
            <body>
                <table border="1">
                    <xsl:for-each select="member[position() mod 4 = 1]">
                        <tr>
                            <xsl:for-each select=". | following-sibling::member[position() &lt; 4]">
                                <td>
                                    <xsl:value-of select="name"/>
                                </td>
                            </xsl:for-each>
                        </tr>
                    </xsl:for-each>
                </table>
            </body>
        </html>
    </xsl:template>
    
    </xsl:stylesheet>
    

    but the result will not look the same for the last, incomplete, row.