Search code examples
xmlxslthtml-tablecell

XSLT to create a table from XML file. Put multiple values in table's cell


This is for a project at school and I'm mostly there. I need to recreate the left table of this PDF document: http://www.canton.edu/courses/Course_Registration_Form.pdf

The problem I'm having is with putting more than one alternate in a cell. It only takes the first alternate element from my XML file. Here is what I have for code:

xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" version="4.0" />
  <xsl:template match="/">
    <html>
      <head>
    <title>Course Registration Form</title>
      </head>
      <body>
    <table border="1">
      <tr>
        <th>CRN#</th>
        <th>Course Number (ENGS 100)</th>
            <th>Section (0W1)</th>
        <th>Title</th>
        <th>Credits</th>
            <th>Alternates</th>
      </tr>
      <xsl:for-each select="/schedule/class">
         <xsl:sort select="crn" />
           <tr> 
        <td><xsl:value-of select="crn" /></td>
        <td><xsl:value-of select="coursenumber" /></td>
        <td><xsl:value-of select="section" /></td>
        <td><xsl:value-of select="title" /></td>
        <td><xsl:value-of select="credits" /></td>
        <td><xsl:value-of select="alternates" /></td>
          </tr>
         </xsl:for-each>
        <tr>
              <td></td>
          <td></td>
          <td></td>
          <td><b>Total Credits</b></td>
          <td><xsl:value-of select="sum(/schedule/class/credits)"/></td>
          <td></td>
        </tr>
        </table>
       </body>
      </html>
      </xsl:template>
 </xsl:stylesheet>

xml:

<!DOCTYPE library SYSTEM "schedule.dtd">
<?xml-stylesheet type="text/xsl" href="schedule.xsl" ?>
<schedule>
    <class>
        <crn>10000</crn>
        <coursenumber>ENGS 100</coursenumber>
        <section>0W1</section>
        <title>Intro to English</title>
        <credits>3</credits>
    </class>
    <class>
        <crn>10001</crn>
        <coursenumber>HIST 100</coursenumber>
        <section>0W2</section>
        <title>Intro to History</title>
        <credits>3</credits>
        <alternates>10011</alternates>
    </class>
    <class>
        <crn>10002</crn>
        <coursenumber>MATH 100</coursenumber>
        <section>0W3</section>
        <title>Intro to Algebra</title>
        <credits>4</credits>
        <alternates>10012</alternates>
        <alternates>10022</alternates>
    </class>
    <class>
        <crn>10003</crn>
        <coursenumber>BSAD 100</coursenumber>
        <section>0W4</section>
        <title>Intro to Business</title>
        <credits>3</credits>
        <alternates>10013</alternates>
        <alternates>10023</alternates>
        <alternates>10033</alternates>
    </class>
</schedule>

DTD:

<!ELEMENT schedule (class+)>
<!ELEMENT class (crn, coursenumber, section, title, credits, alternates?)>
<!ELEMENT crn (#PCDATA)>
<!ELEMENT coursenumber (#PCDATA)>
<!ELEMENT section (#PCDATA)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT credits (#PCDATA)>
<!ELEMENT alternates (#PCDATA)>

What I want it to do is create the table and if there are more than one alternate element in the XML file it will put that in the same cell as the other alternates. Any suggestions or thoughts would be great.


Solution

  • That's because you're using xsl:value-of. In XSLT 1.0, this just returns the first occurrence.

    Try using an xsl:apply-templates instead and adding a template for alternates.

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="html" version="4.0" />
        <xsl:template match="/">
            <html>
                <head>
                    <title>Course Registration Form</title>
                </head>
                <body>
                    <table border="1">
                        <tr>
                            <th>CRN#</th>
                            <th>Course Number (ENGS 100)</th>
                            <th>Section (0W1)</th>
                            <th>Title</th>
                            <th>Credits</th>
                            <th>Alternates</th>
                        </tr>
                        <xsl:for-each select="/schedule/class">
                            <xsl:sort select="crn" />
                            <tr> 
                                <td><xsl:value-of select="crn" /></td>
                                <td><xsl:value-of select="coursenumber" /></td>
                                <td><xsl:value-of select="section" /></td>
                                <td><xsl:value-of select="title" /></td>
                                <td><xsl:value-of select="credits" /></td>
                                <td><xsl:apply-templates select="alternates" /></td>
                            </tr>
                        </xsl:for-each>
                        <tr>
                            <td></td>
                            <td></td>
                            <td></td>
                            <td><b>Total Credits</b></td>
                            <td><xsl:value-of select="sum(/schedule/class/credits)"/></td>
                            <td></td>
                        </tr>
                    </table>
                </body>
            </html>
        </xsl:template>
    
        <xsl:template match="alternates">
            <xsl:if test="not(position()=1)"><br/></xsl:if>
            <xsl:value-of select="."/>
        </xsl:template>
    </xsl:stylesheet>
    

    Using xsl:apply-templates and overriding default templates with other templates (push approach) will be much easier to manage and extend. If your stylesheet will be much more complex, you may want to consider updating the rest of the stylesheet.