views:

76

answers:

3

Hello everyone,

I have the following XML (it is simplified and most attributes are omitted):

<Document>
  <Transfer Name="" From="" To=""/>
  <Transfer Name="" From="" To=""/>
  <OtherElement/>
  <OtherElement/>
  <Flight AirLina="" From="" To=""/>
  <Flight AirLina="" From="" To=""/>
  <OtherElement/>
  <Hotel Name="" Duration=""/>
  <Hotel Name="" Duration=""/>
  <OtherElement/>
  <OtherElement/>
  <Extras Name="" Price=""/>
  <Extras Name="" Price=""/>
  <Extras Name="" Price=""/>
  <Extras Name="" Price=""/>
  <Extras Name="" Price=""/>
  <Extras Name="" Price=""/>
  <OtherElement/>
  <OtherElement/>
</Document>

I have a variable, containing different elements:

<xsl:variable name="packageElements" 
select="/Document/Transfer | /Document/Coach | /Document/Flight | /Document/Hotel | /Document/Extras" />

I would like to display that data in a table with 2 columns. I am using XSLT1.0 and MSXSL processor.

I have been trying it out with the simplest solution I could think of:

<table>
  <tbody>
    <xsl:for-each select="$packageElements[position() mod 2 = 1]">
      <tr>
        <td>
          <!-- current element -->
          <xsl:value-of select="local-name()"/>
        </td>
        <td>
          <!-- element following the current in the $packageElements variable -->
          <!-- Here is where I'm stuck, I can't figure out how to correctly pick it up :( -->
        </td>
      </tr>
    </xsl:for-each>
  </tbody>
</table>

Would really appreciate any help.

Thanks a lot, Dasha

+1  A: 

I think the complexity is in here:

element following the current in the $packageElements variable

This is a node in $packageElements node-set with a position() greater than current node. But, what is the position of the current node in $packegeElements node-set?

Check this. Dimitre builts a expression wich is the count for intersection between preceding nodes (in the document) of current node and the node-set.

Alejandro
+1  A: 

This transformation:

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

 <xsl:variable name="vData" select="/*/*"/>

 <xsl:template match="/">
  <table border="1">
    <xsl:apply-templates select="$vData[position() mod 2 = 1]"/>
  </table>
 </xsl:template>

 <xsl:template match="nums/*">
  <xsl:variable name="vPos" select="position()"/>

  <tr>
    <td><xsl:value-of select="name()"/></td>
    <td><xsl:value-of select="$vData[position() = 2*$vPos]"/></td>
  </tr>
 </xsl:template>
</xsl:stylesheet>

when applied on this XML document:

<nums>
  <A>01</A>
  <num>02</num>
  <B>03</B>
  <num>04</num>
  <C>05</C>
  <num>06</num>
  <D>07</D>
  <num>08</num>
  <E>09</E>
  <num>010</num>
</nums>

produces the wanted, correct result:

<table border="1">
    <tr>
        <td>A</td>
        <td>02</td>
    </tr>
    <tr>
        <td>B</td>
        <td>04</td>
    </tr>
    <tr>
        <td>C</td>
        <td>06</td>
    </tr>
    <tr>
        <td>D</td>
        <td>08</td>
    </tr>
    <tr>
        <td>E</td>
        <td>010</td>
    </tr>
</table>
Dimitre Novatchev
This is not handling (i.e. "ignoring") the `<OtherElement/>` nodes that are unwanted in the output. It handles all `/*/*` nodes as contiguous but in fact they are not. I admit that the input XML is rather sub-optimal to begin with…
Tomalak
@Dimitre Novatchev: Thank you for the answer it was a kick start for my solution.@Tomalak: Igreed that this answer doesn't take into account that I only want selected elements form original XML. I have used your solution for '[XSLT]: Rendering a node sequence as M x N table' post at http://stackoverflow.com/questions/2355952/xslt-rendering-a-node-sequence-as-m-x-n-tableIt's very elegant, I've learned something new - thanks very much!
DashaLuna
@Tomalak: Well, What to do when I absolutely (still) don't understand her XML? This is why I provided my XML document -- to demonstrate the idea -- she probably can catch it and do the rest herself...
Dimitre Novatchev
@DashaLuna: Thanks. I've noticed that you do not define your problems quite understandably. Could you, please, pay more attention to this in your future questions? Well defined problems are easier to understand and solve, you get more solutions in less time. In this question there was no desired output, so I couldn't understand what you actually wanted to do.
Dimitre Novatchev
@Dimitre Novatchev: Sorry about that. I will try my best to describe a problem clearly and with lots of details. Thank you.
DashaLuna
A: 

Alright,

I have combined @Dimitre Novatchev's idea from this post's answers and @Tomalak's from [XSLT]: Rendering a node sequence as M x N table post. I really liked @Tomalak's solution with $perRow variable and the <xsl:template name="filler"> template to cope with empty cells.

With the idea taken from @Dimitre Novatchev answer I'm holding on to $trStartPos. I then calculate $lowerBoundry and $upperBoundry that are used to accumulate all elements that should appear in a row. There might be a more elegant way to do this calculation - please let me know if you come up with one, I would really appreciate it!

XSLT Transformation

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

<!-- Select only required elements -->
<xsl:variable name="tableData" 
     select="/nums/A | /nums/B | /nums/C | /nums/D | /nums/E "/>
<xsl:variable name="perRow" select="2"/>

<xsl:template match="/">
    <table border="1">
        <tbody>
            <xsl:apply-templates 
                select="$tableData[position() mod $perRow = 1]" mode="tr"/>
        </tbody>            
    </table>
</xsl:template>


<xsl:template match="nums/*" mode="tr">
    <xsl:variable name="trStartPos" select="position()" />
    <xsl:variable name="upperBoundry" select="$trStartPos * $perRow" />
    <xsl:variable name="lowerBoundry" select="$upperBoundry - $perRow" />

    <tr>
        <xsl:variable name="tdsData" 
            select="$tableData[(position() &gt; $lowerBoundry) and (position() &lt;= $upperBoundry)]" />
        <xsl:apply-templates select="$tdsData" mode="td"/>
        <!-- fill up the last row - @Tomalak's solution -->
        <xsl:if test="count($tdsData) &lt; $perRow">
            <xsl:call-template name="filler">
                <xsl:with-param name="rest" select="$perRow - count($tdsData)" />
            </xsl:call-template>
        </xsl:if>
    </tr>
</xsl:template>


<!-- Templates for specific elements could be easily added with appropriate info to
     be displayed depending on the element. This one is general just to display
     elements' name and value -->
<xsl:template match="nums/*" mode="td">
    <td>
        El. name: <xsl:value-of select="local-name()"/> -
        El. value: <xsl:value-of select="."/>
    </td>
</xsl:template>

<!-- @Tomalak solution (please read beginning of this answer for reference) -->
<xsl:template name="filler">
    <xsl:param name="rest" select="0" />
    <xsl:if test="$rest">
        <td>&#160;</td>
        <xsl:call-template name="filler">
            <xsl:with-param name="rest" select="$rest - 1" />
        </xsl:call-template>
    </xsl:if>
</xsl:template>
</xsl:stylesheet>

applied on the following XML

<nums>
  <A>A-01</A>
  <num>02</num>
  <num>03</num>
  <num>04</num>
  <B>B-05</B>
  <num>06</num>
  <num>07</num>
  <C>C-08</C>
  <num>09</num>
  <D>D-10</D>
  <num>11</num>
  <num>12</num>
  <num>13</num>
  <E>E-14</E>
  <num>15</num>
</nums>

results in the following output

<table border="1">
    <tbody>
        <tr>
            <td>
                El. name: A -
                El. value: A-01
            </td>
            <td>
                El. name: B -
                El. value: B-05
            </td>
        </tr>
        <tr>
            <td>
                El. name: C -
                El. value: C-08
            </td>
            <td>
                El. name: D -
                El. value: D-10
            </td>
        </tr>
        <tr>
            <td>
                El. name: E -
                El. value: E-14
            </td>
            <td> </td>
        </tr>
    </tbody>
</table>
DashaLuna