3

Here is a fraction of the XML data I am processing

<?xml version="1.0" encoding="utf-16"?>
<ScorecardSummary>
  <DivisionSummary>
    <DivisionName>
      <string> SYSTEM</string>
    </DivisionName>
    <ScorecardSummaryByDivision>
      <ScorecardSummaryByKPI>
        <Header>
          <string>Committed Time of Arrival</string>
          <string>Goal</string>
          <string>1D</string>
          <string>7D</string>
          <string>QTD</string>
          <string>YTD</string>
          <string>YTD Event Cars</string>
        </Header>
        <Data>
          <ScorecardContract>
            <TypeName>System</TypeName>
            <Goal>68</Goal>
            <GoalWarning>64.6</GoalWarning>
            <TotalCountYear>1234</TotalCountYear>
            <Value1D>79</Value1D>
            <Value7D>79.2</Value7D>
            <ValueQTD>79.1</ValueQTD>
            <ValueYTD>73.3</ValueYTD>
          </ScorecardContract>
          <ScorecardContract>
            <TypeName>AG</TypeName>
            <Goal>68</Goal>
            <GoalWarning>64.6</GoalWarning>
            <TotalCountYear>1111</TotalCountYear>
            <Value1D>80.9</Value1D>
            <Value7D>78.7</Value7D>
            <ValueQTD>78.4</ValueQTD>
            <ValueYTD>69.7</ValueYTD>
          </ScorecardContract>

This is a small part of the XSL that produces the tables:

<xsl:template match="ScorecardSummary/DivisionSummary/DivisionName">
  <h1>
    <xsl:value-of select="current()/string"/>
  </h1>
</xsl:template>

<xsl:template match="ScorecardSummaryByDivision">
  <xsl:apply-templates select="current()/ScorecardSummaryByKPI"/>
</xsl:template>

<xsl:template match="ScorecardSummaryByKPI">
  <table border="1" cellspacing="0" cellpadding="5">
    <tr>
      <xsl:choose>
        <xsl:when test="count(preceding-sibling::ScorecardSummaryByKPI) mod 6 &lt; 4">
          <td>
            <table border="1" cellspacing="0" cellpadding="5">
              <xsl:apply-templates select="Header"/>
              <xsl:apply-templates select="Data"/>
            </table>
          </td>
        </xsl:when>
        <xsl:otherwise>
          <td>
            <table border="1" cellspacing="0" cellpadding="5">
              <xsl:apply-templates select="Header"/>
              <xsl:apply-templates select="Data"/>
            </table>
          </td>
        </xsl:otherwise>
      </xsl:choose>
    </tr>
  </table>
</xsl:template>

The XSL produces 6 tables repeatedly like this:

1  
2  
3  
4  
5  
6  

1  
2  
3  
4  
5  
6  

But I want to order them like this:

1 4  
2 5  
3 6  

1 4  
2 5  
3 6  

and so on. I tried using this check, but it doesn't work.

count(preceding-sibling::ScorecardSummaryByKPI) mod 6 &lt; 4

Can anyone help?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Levon Alikhanov
  • 103
  • 4
  • 13
  • Could you please provide a input xml example. – hr_117 May 03 '13 at 07:36
  • @hr_117 I edited the question and added a part of the XML – Levon Alikhanov May 03 '13 at 07:40
  • @hr_117 I didn't provide the whole xsl. It's just a small part of it. – Levon Alikhanov May 03 '13 at 07:44
  • Your question is very unclear. I realise it may be obvious to you, but all we have is a couple of chunks of XML. What do your multiple table entries correspond to? Is there one item per `Data` element? That is the best sense I can make of it, but you are doing calculations on the number of preceding `ScorecardSummaryByKPI` elements, and in your sample there is only one such element. Please clarify. – Borodin May 03 '13 at 08:41
  • @Borodin One table corresponds to **ScorecardSummaryByKPI**. Which contains a **Header** section and a **Data** section. Header section is the header of the table, and Data section contains multiple **ScorecardContract** sections which are the rows of the table. So I need to order those **ScorecardSummaryByKPI**, that's why I do my calculation on them! – Levon Alikhanov May 03 '13 at 08:58
  • @LyovAlikhanov: OK I think I understand. So you want a six-column table for each `ScorecardSummaryByKPI` element that has the data in the `ScorecardContract` elements in each row? But I don't see how that applies to the format you describe. Are you saying you want a *twelve* column table with pairs of data sets on the same row? – Borodin May 03 '13 at 10:26
  • [SCREEN LINK](http://lms.bigbrain.am/elrpN) Here is the screen of what I get. You can see the 6 tables, with blue headers. But they are all one under the other. I want the last three to be ordered just beside the first three! – Levon Alikhanov May 03 '13 at 10:35
  • One table is one **ScorecardSummaryByKPI**. It has 7 headers. And data rows according to the count of **ScorecardContract**. So I have 6 tables! – Levon Alikhanov May 03 '13 at 10:39

2 Answers2

6

Explanation

Your table must have two <td> per row (if you want two columns). Your XSLT does generate only one.

Solution is to interate over one half of the list and generate two <td> per iteration.

So first I would define a size of the table. Example:

  <xsl:param name="size" select="count(catalog/cd)"/>

Then iterate over only a half of it ($size div 2). The number must be rounded if the input list can contain a non-even number of elements: ceiling($size div 2) (Rounding up to catch last element)

      <xsl:for-each select="catalog/cd[ceiling($size div 2) &gt;= position()]">

In each iteration, first render an element itself:

        <td><xsl:value-of select="title"/></td>

Then render an appropriate element from the second half of the table (offset is the number defined before: ceiling($size div 2) Half size of the table)

        <td><xsl:value-of select="following::cd[ceiling($size div 2)]/title"/></td>

You can wrap element rendering in a separate template to avoid code repeating.

Working example

Check this transformation example with W3C XSL TryIt (http://www.w3schools.com/xsl/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog):

<?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="/">
  <xsl:param name="size" select="count(catalog/cd)"/>
  <html>
  <body>
  <h2>My CD Collection</h2>
    <table border="1">
      <tr bgcolor="#9acd32">
        <th>Title</th>
        <th>Title</th>
      </tr>
      <xsl:for-each select="catalog/cd[ceiling($size div 2) &gt;= position()]">
      <tr>
        <td><xsl:value-of select="title"/></td>
        <td><xsl:value-of select="following::cd[ceiling($size div 2)]/title"/></td>
      </tr>
      </xsl:for-each>
    </table>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

It splits CD-catalog (given in example link above) in two columns.

DRCB
  • 2,111
  • 13
  • 21
  • I don't need to count the size, it's constant = 6. so I wrote something like this in the for-each part: **catalog/cd[3 >= position()]** .Yet no result. Visually I see that I have two columns, but my tables are in the first one yet. – Levon Alikhanov May 03 '13 at 09:22
  • I have just tested my example replacing both `ceiling($size div 2)` with `3` (in an iteration select and in second column render). It works and renders a table with 2 coluns and 3 rows, which consists of first 6 items of the list. – DRCB May 03 '13 at 09:26
  • Thank you very much! Finally it worked. Can't vote up yet, I am new to stackoverflow! – Levon Alikhanov May 03 '13 at 11:05
0

Perhaps something like this is what you are looking for: (This only shows the idea, you have to adapt it to your input.)

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

    <xsl:template match="/" >
        <xsl:apply-templates select="//t[count(preceding-sibling::t) &lt; 3]" mode="tables" />

    </xsl:template>
    <xsl:template match="t" >
        {<xsl:value-of select="text()"/>}
    </xsl:template>

    <xsl:template match="t" mode="tables">
        <table border="1">
            <tr>
                <td>
                    <table border="1" >
                        <xsl:apply-templates select="." />
                    </table>
                </td>
                <td>
                    <table border="1">
                        <xsl:apply-templates select="following-sibling::t[count(preceding-sibling::t) = count(current()/preceding-sibling::t) +3]" />
                    </table>
                </td>
            </tr>
        </table>
    </xsl:template>
</xsl:stylesheet>

With this short test input xml:

<?xml version="1.0" encoding="utf-8"?>
<xml>
    <tables>
        <t>1</t>
        <t>2</t>
        <t>3</t>
        <t>4</t>
        <t>5</t>
        <t>6</t>
    </tables>
</xml>

It will generate this output:

<?xml version="1.0"?>
<table xmlns="http://www.w3.org/1999/xhtml" border="1">
  <tr>
    <td>
      <table border="1">
        {1}
    </table>
    </td>
    <td>
      <table border="1">
        {4}
    </table>
    </td>
  </tr>
</table><table xmlns="http://www.w3.org/1999/xhtml" border="1">
  <tr>
    <td>
      <table border="1">
        {2}
    </table>
    </td>
    <td>
      <table border="1">
        {5}
    </table>
    </td>
  </tr>
</table><table xmlns="http://www.w3.org/1999/xhtml" border="1">
  <tr>
    <td>
      <table border="1">
        {3}
    </table>
    </td>
    <td>
      <table border="1">
        {6}
    </table>
    </td>
  </tr>
</table>
hr_117
  • 9,589
  • 1
  • 18
  • 23