BioGeek
BioGeek

Reputation: 22917

Adding rowspan via XSL

I have the following xml:

<?xml version="1.0" encoding="ISO-8859-1"?>

<Loci>
    <Locus>
        <Id>MTBC1726</Id>
        <Alleles>
            <Allele>
                <Name>1.0</Name>
                <Description>No annotation provided</Description>
            </Allele>
            <Allele>
                <Name>2.0</Name>
                <Description>No annotation provided</Description>
            </Allele>
        </Alleles>
    </Locus>
    <Locus>
        <Id>MTBC3142</Id>
        <Alleles>
            <Allele>
                <Name>1.0</Name>
                <Description>No annotation provided</Description>
            </Allele>
        </Alleles>
    </Locus>
</Loci>

And I want to create the following result:

enter image description here

which, in HTML, would look like this:

<html>
<body>

<table border="1">
  <tr>
    <th>Locus</th>
    <th>Allele</th>
    <th>Description</th>
  </tr>
  <tr>
    <td rowspan="2">MTBC1726</td>
    <td>1.0</td>
    <td >No annotation provided</td>
  </tr>
  <tr>
    <td>2.0</td>
    <td >No annotation provided</td>
  </tr>
  <tr>
    <td >MTBC3142</td>
    <td>1.0</td>
    <td >No annotation provided</td>
  </tr>
</table>

</body>
</html>

I have created the following XSL:

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

<xsl:template match="/">
  <html>
  <body>
    <table border="1">
      <tr>
        <th>Locus</th>
        <th>Allele</th>
        <th>Description</th>
      </tr>
      <xsl:for-each select="Loci/Locus">
      <tr>
        <td><xsl:value-of select="Id"/></td>
        <xsl:for-each select="Alleles/Allele">
          <td><xsl:value-of select="Name"/></td>
          <td><xsl:value-of select="Description"/></td>
        </xsl:for-each>
      </tr>
      </xsl:for-each>
    </table>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

but that produces a table like this:

enter image description here

So, my question is: how do I add the rowspan attribute with a count based on the number of <Allele> nodes?

Upvotes: 2

Views: 8062

Answers (3)

Stuart Kershaw
Stuart Kershaw

Reputation: 17701

You could use the XSL count() function to define a variable, and then output that variable as your attribute value:

<xsl:variable name="recordCount" select="count(Alleles/Allele)"/>
<td rowspan="{$recordCount}"><xsl:value-of select="Id"/></td>

Upvotes: 3

Ian Roberts
Ian Roberts

Reputation: 122414

How about this:

<table border="1">
  <tr>
    <th>Locus</th>
    <th>Allele</th>
    <th>Description</th>
  </tr>
  <xsl:for-each select="Loci/Locus">
    <xsl:for-each select="Alleles/Allele">
      <tr>
        <xsl:if test="position() = 1">
          <td rowspan="{last()}">
            <xsl:value-of select="ancestor::Locus[1]/Id"/>
          </td>
        </xsl:if>
        <td><xsl:value-of select="Name"/></td>
        <td><xsl:value-of select="Description"/></td>
      </tr>
    </xsl:for-each>
  </xsl:for-each>
</table>

The idea here is that within the inner for-each you can use position() to see whether you're on the first Allele for this Locus, and last() to give the number of Allele elements that the current Locus contains. The ancestor::Locus[1] is because we need to extract the Locus Id at a point where the current context is its first Allele.

If you want to avoid adding rowspan="1" for single-allele loci, you'll have to use a conditional xsl:attribute instead of an attribute value template:

        <xsl:if test="position() = 1">
          <td>
            <xsl:if test="last() &gt; 1">
              <xsl:attribute name="rowspan">
                <xsl:value-of select="last()" />
              </xsl:attribute>
            </xsl:if>
            <xsl:value-of select="ancestor::Locus[1]/Id"/>
          </td>
        </xsl:if>

Upvotes: 4

Matthew Green
Matthew Green

Reputation: 10401

You can count the number of Allele and use that value to create your rowspan. I also put in a check so that it didn't add a rowspan=1 for cases when there was only the one Allele

<xsl:template match="/">
  <html>
  <body>
    <table border="1">
      <tr>
        <th>Locus</th>
        <th>Allele</th>
        <th>Description</th>
      </tr>
      <xsl:for-each select="Loci/Locus">
      <tr>
        <td>
          <xsl:if test="count(Alleles/Allele) &gt; 1">
            <xsl:attribute name="rowspan">
              <xsl:value-of select="count(Alleles/Allele)"/>
            </xsl:attribute>
          </xsl:if>
          <xsl:value-of select="Id"/>
        </td>
        <xsl:for-each select="Alleles/Allele">
          <td><xsl:value-of select="Name"/></td>
          <td><xsl:value-of select="Description"/></td>
        </xsl:for-each>
      </tr>
      </xsl:for-each>
    </table>
  </body>
  </html>
</xsl:template>

Upvotes: 1

Related Questions