开发者

XSLT1.0 Rendering sequence of different elements stored in a variable as M x N table

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.


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.


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: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>


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">
<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>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜