代码之家  ›  专栏  ›  技术社区  ›  249076

使用模板而不是每个模板对XML数据进行简单分组的XSLT到HTML

  •  3
  • 249076  · 技术社区  · 15 年前

    自从我成为一名SharePoint管理员以来,我一直在不停地处理XSLT,它使用XSLT ALOT来显示列表数据。我最近开始使用它来转换数据库结果,我使用扩展方法将这些结果转换为XML。我正在尝试生成干净的HTML。

    我的第一次尝试,效果很好。不管我在各地使用的是什么,从那以后我读到这是一件坏事。我读了很多关于使用钥匙的东西,但我不明白或者无法让它发挥作用。所以我重写了这个样式表,下面是它下面的样式表。它使用每个模板都没有的模板。

    <?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 回复  |  直到 15 年前
        1
  •  1
  •   Lucero    15 年前

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

    通过使用模板,所有匹配的节点(不仅仅是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', .))]" />
    
        2
  •  1
  •   user357812    15 年前

    “for each”模板是XSLT的一个很好的特性。

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

    在您的示例中很清楚:一个简单的“模板”和许多指导流程的“针对每个模板”。

    XSLT本身的关键用法是性能。它的作用在于替换涉及到输入树中许多节点重复传输的XPath表达式。Muenchian分组方法是键的一种特殊用法。简单分组可以在不使用键的情况下进行优化。

    另一方面,人口是一个特殊的转型案例。我认为最好保持将XHTML语义与XSLT转换分开。例如,请访问www.arnedabieesraces.com.ar。