XSLT1.0将存储在变量中的不同元素的渲染序列作为M x N表

时间:2010-06-08 12:38:06

标签: xslt html-table rendering xslt-1.0 sequence

我有以下XML(它是简化的,省略了大多数属性):

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

我有一个变量,包含不同的元素:

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

我想在包含2列的表中显示该数据。我正在使用XSLT1.0和MSXSL处理器。

我一直在尝试用我能想到的最简单的解决方案:

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

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:1)

我认为复杂性就在这里:

  

元素跟在$ packageElements变量中的当前

之后

这是$ packageElements节点集中的一个节点,其position()大于当前节点。但是,$ packegeElements node-set 中当前节点的位置是什么?

检查this。 Dimitre构建一个表达式,它是当前节点的先前节点(在文档中)与节点集之间的交集计数。

答案 1 :(得分:1)

此转化

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

应用于此XML文档时

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

生成想要的正确结果

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

答案 2 :(得分:1)

好的,

我将@Dimitre Novatchev的想法与来自 [XSLT]: Rendering a node sequence as M x N table 帖子的帖子的答案和@ Tomalak's结合起来。我非常喜欢使用$perRow变量的@ Tomalak解决方案和<xsl:template name="filler">模板来处理空单元格。

根据@Dimitre Novatchev提出的想法回答我坚持$trStartPos。然后,我计算$lowerBoundry$upperBoundry,用于累积应出现在行中的所有元素。可能有更优雅的方式进行此计算 - 如果您想出一个,请告诉我,我真的很感激!

XSLT转型

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

应用于以下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>

会产生以下输出

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