XSLT动态创建行和列

时间:2012-10-12 19:49:13

标签: xslt

我有一个格式如下的XML文件:

 <DataSet>
  <Data id ="1" columns ="4">
    <item name ="data1" value="value1"/>
    <item name ="data2" value="value2"/>
    <item name ="data3" value="value3"/>
    <item name ="data4" value="value4"/>
    <item name ="data5" value="value5"/>
  </Data>
  <Data id="2" columns ="2">
    <item name ="data1" value="value1"/>
    <item name ="data2" value="value2"/>
    <item name ="data3" value="value3"/>
    <item name ="data4" value="value4"/>
  </Data>
</DataSet>

我需要一个XSL转换来获得以下表结构。这里的想法是在两个相邻的单元格中显示名称和值属性。因此,'item'将与2列相关联,而一行将包含两个项目的名称/值对。列数将在数据元素中指定,并且始终是2的倍数。

<report>
  <table>
    <tr>
      <td>data1</td>
      <td>value1</td>
      <td>data2</td>
      <td>value2</td>
    </tr>
    <tr>
      <td>data3</td>
      <td>value3</td>
      <td>data4</td>
      <td>value4</td>
    </tr>
    <tr>
      <td>data5</td>
      <td>value5</td>
      <td></td>
      <td></td>
    </tr>
 </table>
 <table>
    <tr>
      <td>data1</td>
      <td>value1</td>
    </tr>
    <tr>
      <td>data2</td>
      <td>value2</td>
    </tr>
    <tr>
      <td>data3</td>
      <td>value3</td>
    </tr>
    <tr>
      <td>data4</td>
      <td>value4</td>
    </tr>
  </table>
</report>

3 个答案:

答案 0 :(得分:1)

应用于提供的输入的以下XSL转换会生成所需的输出。下面提供了一些解释。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/DataSet"><report>
        <xsl:apply-templates select="@*|node()" />
    </report></xsl:template>

    <xsl:template match="Data">
        <xsl:variable name="count" select="count(item)" />
        <xsl:variable name="M" select="@columns div 2" />
        <xsl:variable name="N" select="($count + ($count mod $M)) div $M" />
        <table>
            <xsl:call-template name="nth-row">
                <xsl:with-param name="n" select="1" />
                <xsl:with-param name="M" select="$M" />
                <xsl:with-param name="N" select="$N" />
            </xsl:call-template>
        </table>
    </xsl:template>

    <xsl:template name="nth-row">
        <xsl:param name="n" />
        <xsl:param name="N" />
        <xsl:param name="M" />
        <tr>
            <xsl:call-template name="nmth-cell">
                <xsl:with-param name="n" select="$n" />
                <xsl:with-param name="m" select="1" />
                <xsl:with-param name="N" select="$N" />
                <xsl:with-param name="M" select="$M" />
            </xsl:call-template>
        </tr>
        <xsl:if test="$N > $n">
            <xsl:call-template name="nth-row">
                <xsl:with-param name="n" select="$n + 1" />
                <xsl:with-param name="N" select="$N" />
                <xsl:with-param name="M" select="$M" />
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

    <xsl:template name="nmth-cell">
        <xsl:param name="n" />
        <xsl:param name="m" />
        <xsl:param name="N" />
        <xsl:param name="M" />
        <xsl:variable name="pos" select="($n - 1) * $M + $m" />
        <xsl:choose>
            <xsl:when test="item[position()=$pos]">
                <td><xsl:value-of select="item[position()=$pos]/@name" /></td>
                <td><xsl:value-of select="item[position()=$pos]/@value" /></td>
            </xsl:when>
            <xsl:otherwise>
                <td></td>
                <td></td>
            </xsl:otherwise>
        </xsl:choose>
        <xsl:if test="$M > $m">
            <xsl:call-template name="nmth-cell">
                <xsl:with-param name="n" select="$n" />
                <xsl:with-param name="m" select="$m + 1" />
                <xsl:with-param name="N" select="$N" />
                <xsl:with-param name="M" select="$M" />
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:transform>
  1. 匹配/DataSet会生成根元素<report />并继续应用模板。

  2. Data内部匹配/DataSet会为每个<table />元素生成<Data />,然后通过调用名为nth-row的模板启动有趣的部分。使用的变量和参数是:

    • n :当前行的编号,从1开始
    • M :根据属性@columns除以2计算的列数,因为每个<item />会产生两个<td />元素。
    • N :行数,根据<item />元素的数量计算并除以 M 。要考虑div截断整数值,余额$count mod $M之前会添加到$count
  3. 现在出现了一些递归模板调用。每次调用nth-row时,它都会输出<tr />,然后使用适当的参数调用nmth-cell。只要当前行不是最后一行,nth-row将以$n的递增值递归调用。

  4. 最后,模板nmth-cell每次调用时都会输出两个包含相应<td />值的<item />元素,如果没有相应的<item />则不输出}。只要当前列不是最后一列,nmth-cell将以递增的值$m递归调用。

  5. 我希望这会有所帮助。如果有任何问题或者您不清楚,请随意询问。

答案 1 :(得分:1)

这是一个更简单的解决方案。

此XSLT 1.0样式表......

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

<xsl:template match="/*">
  <report>
    <xsl:apply-templates />
  </report>
</xsl:template>

<xsl:template match="Data">
  <xsl:variable name="cols" select="@columns" />
  <table>
    <xsl:for-each select="item[position()*2 mod $cols = (2 mod $cols)]">
      <tr>
       <xsl:for-each select="(.|following-sibling::item)
          [ position()*2 &lt;= $cols]">
         <td><xsl:value-of select="@name" /></td>  
         <td><xsl:value-of select="@value" /></td>  
       </xsl:for-each> 
       <xsl:if test="position()=last()">
         <xsl:for-each select="((/)//*)[position() &lt;=
              ($cols - (count(.|following-sibling::item)*2))]">
            <td />
         </xsl:for-each>   
       </xsl:if>  
      </tr>  
    </xsl:for-each>  
  </table>
</xsl:template>

</xsl:stylesheet>

...应用于此输入时...

<DataSet>
  <Data id ="1" columns ="4">
    <item name ="data1" value="value1"/>
    <item name ="data2" value="value2"/>
    <item name ="data3" value="value3"/>
    <item name ="data4" value="value4"/>
    <item name ="data5" value="value5"/>
  </Data>
  <Data id="2" columns ="2">
    <item name ="data1" value="value1"/>
    <item name ="data2" value="value2"/>
    <item name ="data3" value="value3"/>
    <item name ="data4" value="value4"/>
  </Data>
</DataSet>   

<强> ...产量...

<report>
  <table>
    <tr>
      <td>data1</td>
      <td>value1</td>
      <td>data2</td>
      <td>value2</td>
    </tr>
    <tr>
      <td>data3</td>
      <td>value3</td>
      <td>data4</td>
      <td>value4</td>
    </tr>
    <tr>
      <td>data5</td>
      <td>value5</td>
      <td></td>
      <td></td>
    </tr>
  </table>
  <table>
    <tr>
      <td>data1</td>
      <td>value1</td>
    </tr>
    <tr>
      <td>data2</td>
      <td>value2</td>
    </tr>
    <tr>
      <td>data3</td>
      <td>value3</td>
    </tr>
    <tr>
      <td>data4</td>
      <td>value4</td>
    </tr>
  </table>
</report>

说明

  1. 您可以非常简单地使用position()将输出结构化为矩阵。这比具有许多参数的错综复杂的调用模板更为可取。
  2. 使用html输出方法而不是默认的xml输出方法。这为您提供了空元素的html编码风格(例如<td></td> over xml style </td>)。
  3. 使用Piez Method在表格的最后一行发出奇数空表格单元格。

答案 2 :(得分:0)

我不知道你想要什么样的格式,但这应该让你接近我希望。它为每个数据集创建一个表,其中数据和值对彼此相邻。如果您还需要格式化帮助,请发表评论

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
  <html>
  <body>
  <report>
      <xsl:for-each select="DataSet/Data">
      <table>
          <xsl:for-each select="item">
              <tr>
                  <td><xsl:value-of select="@name"/></td>
                  <td><xsl:value-of select="@value"/></td>
              </tr>
          </xsl:for-each>
       </table>
       </xsl:for-each>
  </report>
  </body>
  </html>
</xsl:template>