1

我有一个具有以下格式的 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 转换来获得以下表格结构。这里的想法是在两个相邻的单元格中显示名称和值属性。因此,一个“项目”将与 2 列相关联,一行将包含两个项目的名称/值对。列数将在 Data 元素中指定,并且始终是 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>
4

3 回答 3

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被调用时,它会输出 a <tr />,然后nmth-cell使用适当的参数调用。只要当前行不是最后一行,nth-row就会以递增的值递归调用$n.

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

我希望这有帮助。随意问,如果这有什么问题或不清楚。

于 2012-10-12T20:00:53.350 回答
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>xml 风格</td>)。
  3. 使用Piez 方法在表格的最后一行发出奇数的空表格单元格。
于 2012-10-14T13:36:53.713 回答
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>

于 2012-10-12T20:02:41.767 回答