使用模板而不是for-each对XML数据进行简单分组的XSLT转换为HTML

时间:2010-06-09 14:33:56

标签: xml xslt xpath msxml xslt-1.0

自从我成为一名sharepoint管理员以来,我一直在乱用xslt,它使用xslt很多来显示列表数据。我最近开始使用它来转换我使用扩展方法转换为xml的数据库结果。我正在尝试制作干净的HTML。

我的第一次尝试,工作得很好。不过我用过 - 每个地方,我读过这是一件坏事。我读了很多关于使用密钥的东西,但是我无法理解或者让它工作。所以我把这个样式表改写成下面的样式表。它使用的模板没有for-each。

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

  <xsl:template match="/">


<html>
  <head>
    <link rel="Stylesheet" type="text/css" href="../styles/BoxReportStyle.css" />
  </head>
  <body>


    <span class="BoxReport">
      <h2>Checked Out Boxes by Department with Transaction History</h2>

      Count=<xsl:value-of select="count( /CheckedOutBoxes/row ) "/>

    <!-- Get the divisions, since we are groing to group by division-->
    <xsl:variable name="DivisionList" select="/CheckedOutBoxes/row[ not( Division = preceding-sibling::row/Division ) ]/Division" />

    <xsl:for-each select="$DivisionList">

      <xsl:variable name="DivisionName" select="." />

      <h3>
        <xsl:value-of disable-output-escaping="yes" select="$DivisionName "/>
      </h3>

      <!-- Get the list of departments, so we can group by department -->
      <xsl:variable name="DepartmentList" select="/CheckedOutBoxes/row[ Division = $DivisionName and not( Department = preceding-sibling::row/Department) ]/Department" />

      <xsl:for-each select="$DepartmentList">
        <xsl:variable name="DepartmentName" select="." />

        <h4>
          <xsl:value-of disable-output-escaping="yes" select="$DepartmentName"/>
        </h4>

        <xsl:variable name="Rows" select="/CheckedOutBoxes/row[ Division = $DivisionName and Department = $DepartmentName ]" />

        <!-- Start displaying the checked out box information for this division and department -->
        <table>
          <th>Box Number</th>
          <th>Status Name</th>
          <th>Entry Date</th>
          <th>Description</th>

          <xsl:for-each select="$Rows">

            <tr>
              <td>
                <xsl:value-of select="BoxNumber"/>
              </td>
              <td>
                <xsl:value-of select="StatusName"/>
              </td>
              <td>
                <xsl:value-of select="EntryDate"/>
              </td>
              <td width="200px">
                <xsl:value-of disable-output-escaping="yes" select="Description"/>
              </td>

            </tr>

            <!-- Now display the transaction history if there is any-->
            <xsl:if test=" count( Transaction ) > 0 ">
              <tr>
                <td></td> <!-- One blank row to shift things over-->
                <td colspan="3">
                  <!-- Display transaction table-->
                  <table class="SubTable">
                    <th>Transaction Date</th>
                    <th>Requestor</th>
                    <th>Comments</th>

                    <xsl:for-each select="Transaction" >
                      <tr>
                        <td>
                          <xsl:value-of select="TransactionDate"/>
                        </td>
                        <td>
                          <xsl:value-of select="Requestor"/>
                        </td>
                        <td width="200px">
                          <xsl:value-of disable-output-escaping="yes" select="Comments"/>
                        </td>
                      </tr>
                    </xsl:for-each>
                  </table>
                </td>
              </tr>
            </xsl:if>
          </xsl:for-each>
        </table>

      </xsl:for-each>

    </xsl:for-each>
    </span>


  </body>

</html>


  </xsl:template>



</xsl:stylesheet>

我现在已经改写了这个:

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



 <xsl:template match="/">
<html>
  <head>
    <link rel="Stylesheet" type="text/css" href="../styles/BoxReportStyle.css" />
  </head>
  <body>
    <span class="BoxReport">

      <h2>Checked Out Boxes by Department with Transaction History</h2>

      Count=<xsl:value-of select="count( /CheckedOutBoxes/row ) "/>

      <xsl:apply-templates mode="Division" select="/CheckedOutBoxes/row[ not( Division = preceding-sibling::row/Division ) ]"></xsl:apply-templates>


    </span>
  </body>
</html>
  </xsl:template>

 <xsl:template mode="Division" match="row">
<h3>
  <xsl:value-of select="Division" disable-output-escaping="yes"/>
</h3>

<xsl:variable name="DivisionName" select="Division" />

<xsl:apply-templates mode="Department" select="/CheckedOutBoxes/row[ Division = $DivisionName and not( Department = preceding-sibling::row/Department ) ]"></xsl:apply-templates>

</xsl:template>

 <xsl:template mode="Department" match="row">
<h4>
  <xsl:value-of select="Department" disable-output-escaping="yes"/>
</h4>

<xsl:variable name="DivisionName" select="Division" />
<xsl:variable name="DepartmentName" select="Department" />

<table>
  <th>Box Number</th>
  <th>Status Name</th>
  <th>Entry Date</th>
  <th>Description</th>

  <xsl:apply-templates mode="row" select="/CheckedOutBoxes/row[ Division = $DivisionName and Department = $DepartmentName ]" ></xsl:apply-templates>

  </table>



</xsl:template>

<xsl:template mode="row" match="row">

<tr>
  <td>
    <xsl:value-of select="BoxNumber"/>
  </td>
  <td>
    <xsl:value-of select="StatusName"/>
  </td>
  <td>
    <xsl:value-of select="EntryDate"/>
  </td>
  <td width="200px">
    <xsl:value-of disable-output-escaping="yes" select="Description"/>
  </td>

</tr>

<!-- Display Transaction stuff as another row if we have any -->
<xsl:if test=" count( Transaction ) > 0 ">
  <tr>
    <td></td><!-- Shift the transaction over-->
    <td colspan="3">
      <!-- Start Transaction Table -->
      <table class="SubTable">
        <th>Transaction Date</th>
        <th>Requestor</th>
        <th>Comments</th>

        <xsl:apply-templates select="Transaction">
          <xsl:sort order="descending" select="TransactionDate"/>
        </xsl:apply-templates>
      </table>
    </td>
  </tr>
</xsl:if>

</xsl:template>


<xsl:template match="Transaction">
<tr>
  <td>
    <xsl:value-of select="TransactionDate"/>
  </td>
  <td>
    <xsl:value-of select="Requestor"/>
  </td>
  <td width="200px">
    <xsl:value-of disable-output-escaping="yes" select="Comments"/>
  </td>
</tr>
  </xsl:template>

</xsl:stylesheet>

我没有包含样本输入和输出,因为这是全部自动生成的。如果需要,我可以花很多时间,并尝试制作一些东西。

我的问题是,这是一个更好的方法吗?此外,如果关键方式更好,有人可以解释,或提供一个良好解释的链接?

2 个答案:

答案 0 :(得分:1)

基本上使用for-each与模板的问题归结为创建可重用,更通用的转换。

通过使用模板,所有匹配的节点 - 不仅仅是for-each中明确使用的节点 - 都可以从模板中受益,这有助于避免重复代码,同时将工作表分成更易于管理的较小单元。它实际上几乎与拥有一个庞大的程序或较小的程序在命令式编程中相互调用一样。

虽然有些人认为在某些引擎中使用模板可能会表现得更好,但我相信这并没有真正发挥作用。

也就是说,您可能想要了解muenchian method(使用键)来实际分组您重复键的数据。在某些引擎上使用preceding-sibling轴的速度非常慢,因此在不是绝对需要时最好避免使用它。

这样的事情应该为分裂做好准备(未经测试):

<xsl:key name="divisions" match="/CheckedOutBoxes/row/Division" use="." />

...

<xsl:apply-templates mode="Division" select="/CheckedOutBoxes/Division[generate-id(.)=generate-id(key('divisions', .))]" />

答案 1 :(得分:1)

“for-each”模板是XSLT的一个很好的功能。

使用“模板”而不是“for-each”的建议主要是关于可能滥用XSLT处理模型。

在你的例子中很清楚:一个简单的“模板”和许多“为每个人”指导这个过程。

XSLT本身的关键用法是关于性能。它的用处在于替换涉及重复传输输入树的许多节点的XPath表达式。 Muenchian分组方法是密钥的特殊用途。无需使用密钥即可进行简单的分组。

另一方面,人口是转型的特例。我认为维护将XHTML语义与XSLT转换分开是更好的。请访问www.aranedabienesraices.com.ar作为示例。