1

I have following piece of XML file:

<PASS>
<FIRMWARE Unit="1" Group="FIRM">
  <test name="ACPI" ID="ACPI" />
</FIRMWARE>
<NETWORK Unit="2" Group="NTWK">
  <test name="Controller Test" ID="Ctlr" />
</NETWORK>
<NETWORK Unit="1" Group="NTWK">
  <test name="Controller Test" ID="Ctlr" />
</NETWORK>
<ATA Unit="1" Group="ATA-">
  <test name="Serial Controllers" ID="SATA" />
</ATA>
<PARALLEL_PORT Unit="1" Group="LPT-">
  <test name="Verify Controller" ID="Ctrl" />
  <test name="Check Status Port" ID="Stat" />
  <test name="Interrupt Test" ID="Int-" />
</PARALLEL_PORT>
<SERIAL_PORT Unit="4" Group="COM-">
  <test name="Interrupt" ID="Intr" />
  <test name="Line Control" ID="Line" />
  <test name="Test Loopback" ID="LpBk" />
  <test name="Test Internal FIFO" ID="FIFO" />
  <test name="Test Internal Loopback" ID="ILBk" />
</SERIAL_PORT>
<SERIAL_PORT Unit="3" Group="COM-">
  <test name="Interrupt" ID="Intr" />
  <test name="Line Control" ID="Line" />
  <test name="Test Loopback" ID="LpBk" />
  <test name="Test Internal FIFO" ID="FIFO" />
  <test name="Test Internal Loopback" ID="ILBk" />
</SERIAL_PORT>
</PASS>

With the existing XSL file I get all test results in one column. This is not very practical for printing and so on. How can I create a table with 2 or more columns?

<xsl:template match="PASS">
<div class="component">
  <h4>PASSED</h4>
  <xsl:call-template name="outputtable">
    <xsl:with-param name="result" select="'PASSED'" />
  </xsl:call-template>
</div>
</xsl:template>

<xsl:template name="outputtable">
<div class="attributes">
  <xsl:variable name="resultlist" select="*" />
  <xsl:variable name="index" select="count(child::*)" />
  <xsl:for-each select="$resultlist">
    <xsl:variable name="Unit" select="@Unit" />
    <xsl:variable name="Group" select="@Group" />
    <p class="attrtitle">
      <xsl:value-of select="name()" />
      Unit:
      <xsl:value-of select="$Unit" />
    </p>
    <xsl:variable name="testtype" select="*" />
    <xsl:for-each select="$testtype">
      <p class="attribute">
        <xsl:value-of select="@name" />
      </p>
    </xsl:for-each>
    <p />
  </xsl:for-each>
</div>

I've already searched for solution and found following links: Use xslt to convert xml list into html table with multiple columns and http://blogs.msdn.com/b/kaevans/archive/2003/04/03/4754.aspx

The situation I have is rather different I have structured objects and not simple as "Item" or "ComputerName" and the other drawback is that those test are not of the same type (, etc.). This XML report is constructed automatically by non-OSS software, so I have no way to change it at creation time.

I'll be glad to get any ideas!

2
  • Can you post the desired output for your example? Commented May 19, 2011 at 10:59
  • Good question, +1. See my answer for a complete and easy solution. Detailed explanation is also provided. Learn to use templates instead of <xsl:for-each> -- templates are much more powerful and elegant. Commented May 19, 2011 at 12:34

1 Answer 1

0

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="pnumCols" select="3"/>

    <xsl:template match="PASS">
        <div class="component">
            <h4>PASSED</h4>
            <div class="attributes">
             <table border="1">
                <xsl:apply-templates select=
                  "*[position() mod $pnumCols = 1]"/>
             </table>
            </div>
        </div>
    </xsl:template>

    <xsl:template match="PASS/*">
     <tr>
      <xsl:apply-templates mode="process" select=
       ".|following-sibling::*[not(position() > $pnumCols -1)]"/>
     </tr>
    </xsl:template>

    <xsl:template match="PASS/*" mode="process">
     <xsl:variable name="Unit" select="@Unit" />
     <xsl:variable name="Group" select="@Group" />
     <td>
         <p class="attrtitle">
            <xsl:value-of select="name()" />       Unit:
            <xsl:value-of select="$Unit" />
         </p>
         <xsl:variable name="testtype" select="*" />
         <xsl:for-each select="$testtype">
            <p class="attribute">
                <xsl:value-of select="@name" />
            </p>
         </xsl:for-each>
         <p />
     </td>
    </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<PASS>
    <FIRMWARE Unit="1" Group="FIRM">
        <test name="ACPI" ID="ACPI" />
    </FIRMWARE>
    <NETWORK Unit="2" Group="NTWK">
        <test name="Controller Test" ID="Ctlr" />
    </NETWORK>
    <NETWORK Unit="1" Group="NTWK">
        <test name="Controller Test" ID="Ctlr" />
    </NETWORK>
    <ATA Unit="1" Group="ATA-">
        <test name="Serial Controllers" ID="SATA" />
    </ATA>
    <PARALLEL_PORT Unit="1" Group="LPT-">
        <test name="Verify Controller" ID="Ctrl" />
        <test name="Check Status Port" ID="Stat" />
        <test name="Interrupt Test" ID="Int-" />
    </PARALLEL_PORT>
    <SERIAL_PORT Unit="4" Group="COM-">
        <test name="Interrupt" ID="Intr" />
        <test name="Line Control" ID="Line" />
        <test name="Test Loopback" ID="LpBk" />
        <test name="Test Internal FIFO" ID="FIFO" />
        <test name="Test Internal Loopback" ID="ILBk" />
    </SERIAL_PORT>
    <SERIAL_PORT Unit="3" Group="COM-">
        <test name="Interrupt" ID="Intr" />
        <test name="Line Control" ID="Line" />
        <test name="Test Loopback" ID="LpBk" />
        <test name="Test Internal FIFO" ID="FIFO" />
        <test name="Test Internal Loopback" ID="ILBk" />
    </SERIAL_PORT>
</PASS>

produces the results in $pnumCols-column format, in this specific case pnumCols is 3:

<div class="component">
   <h4>PASSED</h4>
   <div class="attributes">
      <table border="1">
         <tr>
            <td>
               <p class="attrtitle">FIRMWARE       Unit:
            1</p>
               <p class="attribute">ACPI</p>
               <p/>
            </td>
            <td>
               <p class="attrtitle">NETWORK       Unit:
            2</p>
               <p class="attribute">Controller Test</p>
               <p/>
            </td>
            <td>
               <p class="attrtitle">NETWORK       Unit:
            1</p>
               <p class="attribute">Controller Test</p>
               <p/>
            </td>
         </tr>
         <tr>
            <td>
               <p class="attrtitle">ATA       Unit:
            1</p>
               <p class="attribute">Serial Controllers</p>
               <p/>
            </td>
            <td>
               <p class="attrtitle">PARALLEL_PORT       Unit:
            1</p>
               <p class="attribute">Verify Controller</p>
               <p class="attribute">Check Status Port</p>
               <p class="attribute">Interrupt Test</p>
               <p/>
            </td>
            <td>
               <p class="attrtitle">SERIAL_PORT       Unit:
            4</p>
               <p class="attribute">Interrupt</p>
               <p class="attribute">Line Control</p>
               <p class="attribute">Test Loopback</p>
               <p class="attribute">Test Internal FIFO</p>
               <p class="attribute">Test Internal Loopback</p>
               <p/>
            </td>
         </tr>
         <tr>
            <td>
               <p class="attrtitle">SERIAL_PORT       Unit:
            3</p>
               <p class="attribute">Interrupt</p>
               <p class="attribute">Line Control</p>
               <p class="attribute">Test Loopback</p>
               <p class="attribute">Test Internal FIFO</p>
               <p class="attribute">Test Internal Loopback</p>
               <p/>
            </td>
         </tr>
      </table>
   </div>
</div>

Explanation: We use two templates -- one for wrapping a group of $pnumCols consecutive results in a row, the other, in mode="process", to actually produce the contents within that row.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.