使用for-each-group实现高性能XSLT

时间:2012-07-15 10:15:35

标签: xslt xpath xslt-2.0

我有一个XSLT(1.0)样式表。它没有问题。我想把它变成2.0。我想使用xsl:for-each-group(并使其具有高性能)。有可能的?怎么样?请解释一下。

我有很多地方,比如

    <xsl:if test="test condition">
     <xsl:for-each select="wo:tent">
     <width aidwidth='{/wo:document/styles [@wo:name=current()/@wo:style-name]/@wo:width}'
</xsl:for-each>
    </xsl:if>

ADDED

<xsl:template match="wo:country">
            <xsl:for-each select="@*">
                <xsl:copy/>
            </xsl:for-each>
            <xsl:variable name="states" select="wo:pages[@xil:style = &quot;topstates&quot; or @xil:style = &quot;toppage-title&quot;]"/>
            <xsl:variable name="provinces" select="wo:pages[@xil:style = &quot;topprovinces&quot;]"/>
            <xsl:choose>
                <xsl:when test="$states">
                    <xsl:apply-templates select="$states[2]/preceding-sibling::*"/>
                    <xsl:apply-templates select="$states[2]" mode="states">
                        <xsl:with-param name="states" select="$states[position() != 0]"/>
                    </xsl:apply-templates>
                </xsl:when>
                <xsl:when test="$provinces">
                    <xsl:apply-templates select="$provinces[2]/preceding-sibling::*"/>
                    <xsl:apply-templates select="$provinces[2]" mode="provinces">
                        <xsl:with-param name="provinces" select="$provinces[position() != 2]"/>
                    </xsl:apply-templates>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates/>
                </xsl:otherwise>
            </xsl:choose>
    </xsl:template>

消息来源

<?xml version="1.0" encoding="UTF-8"?>
<wo:country>
   some stuff
</wo:country>

2 个答案:

答案 0 :(得分:7)

我假设你想要深入描述xsl:for-each-group以及如何使用它。如果这不是您要求的,请告诉我。

XSLT 2.0中的新指令采用一组项目并对它们进行分组。这组项目被称为&#34;人口&#34;,这些组只被称为组。该指令依次处理每个组。

xsl:for-each-group指令的可能属性包括:

  1. 选择
  2. group-by
  3. 组相邻的
  4. 基团的起始-与
  5. 基的结束-与
  6. 整理
  7. @select是强制性的。其他是可选的。它可以使用任意数量的xsl:sort子(但它们必须先出现),然后是序列构造函数。 A&#34;序列构造函数&#34;是模板之类的所有序列发射类型指令的术语。

    @select

    select属性指定一个XPATH表达式,该表达式的计算结果为要分组的总体。

    @基的由

    group-by属性指定XPATH表达式,当分组类型为公共值时使用该表达式。人口中评估为与另一个人具有相同分组值的每个项目都与另一个项目位于同一组中。

    XSLT 1.0当分组类型按公共值分组时,Muenchian分组并不太难。有两种更常见的分组形式:按相似值对相邻项目进行分组;并对一组相邻的项目进行分组,这些项目的组在结束时或在开始时通过某种测试划分。虽然这两种形式的分组仍然可以与Muenchian一起使用,但它变得相对复杂。关于这些类型的Muenchian在规模上效率也会降低,因为使用了兄弟轴(不过你拼写了!)。

    想到XSLT 2.0的另一个优点是Muenchian只适用于节点集,而xsl:for-each-group在应用程序中更广泛,因为它适用于一系列,而不是只是节点。

    @ group-by表达式的结果将是一系列项目。该序列被雾化和去除。正在测试的人口项目将是每个值的一个组的成员。这是一个奇怪的结果,使用@ group-by,item可能是多个组的成员,或者甚至没有。虽然我怀疑你可以在XSLT 2.0中做的任何事情,你可以通过一些曲折的路径,在XSLT 1.0中,将一个项目分成两组的能力在XSLT 1.0 Muenchian中是非常繁琐的。

    @组相邻的

    group-by,group-adjacent,group-starting-with和group-ending-with属性是互斥的,因为它们指定了不同类型的分组。具有公共值且在群体中相邻的项目被组合在一起。与@ group-by不同,@ group-adjacent必须在雾化后评估为单个原子值。

    基团的起始-以

    与select,group-adjacent和group-by不同,此属性不指定XPATH选择表达式,而是指定模式,与xsl:template / @ match指定模式的方式相同,一个选择。如果人口中的项目通过模式测试或是人口中的第一项,则会启动新组。否则,该项目将继续前一项目中的组。

    Martin提到了规范示例(w3.org/TR/xslt20/#grouping-example)。从该参考文献中,我将复制标题为&#34;通过其初始元素识别组的示例&#34;,但稍微改变它以强调关于人口的初始项目的观点。

    所以这是我们的输入文档(从w3规范中复制。包含孤立行是我的)...

    <body>
      <p>This is an orphaned paragraph.</p>
      <h2>Introduction</h2>
      <p>XSLT is used to write stylesheets.</p>
      <p>XQuery is used to query XML databases.</p>
      <h2>What is a stylesheet?</h2>
      <p>A stylesheet is an XML document used to define a transformation.</p>
      <p>Stylesheets may be written in XSLT.</p>
      <p>XSLT 2.0 introduces new grouping constructs.</p>
    </body>
    

    ...我们想要做的是将组定义为以h2开头的节点,并包括以下所有p直到下一个h2。 w3给出的示例解决方案是使用@ group-starting-with ...

    <xsl:template match="body">
      <chapter>
            <xsl:for-each-group select="*" group-starting-with="h2"      >
              <section title="{self::h2}">
                <xsl:for-each select="current-group()[self::p]">
                  <para><xsl:value-of select="."/></para>
                </xsl:for-each> 
              </section>
            </xsl:for-each-group>
      </chapter>
    </xsl:template>
    

    在规范示例中,当输入不包含孤立行时,会产生所需的结果......

    <chapter>
      <section title="Introduction">
        <para>XSLT is used to write stylesheets.</para>
        <para>XQuery is used to query XML databases.</para>
      </section> 
      <section title="What is a stylesheet?">
        <para>A stylesheet is an XML document used to define a transformation.</para>
        <para>Stylesheets may be written in XSLT.</para>
        <para>XSLT 2.0 introduces new grouping constructs.</para>
      </section>
    </chapter>
    

    虽然在我们的特定情况下,我们得到了......

    <chapter>
       <section title="">
          <para>This is an orphaned paragraph.</para>
       </section>
       <section title="Introduction">
          <para>XSLT is used to write stylesheets.</para>
          <para>XQuery is used to query XML databases.</para>
       </section>
       <section title="What is a stylesheet?">
          <para>A stylesheet is an XML document used to define a transformation.</para>
          <para>Stylesheets may be written in XSLT.</para>
          <para>XSLT 2.0 introduces new grouping constructs.</para>
       </section>
    </chapter>
    

    如果不希望出现孤立线的初始部分,则可以使用简单的解决方案。我现在不打算进入他们。我的观点是要强调这样一个事实,即@ group-starting-with产生的第一组可能是一个孤儿&#39;组。 “孤儿”是指一个头节点不符合指定模式的组。

    @collat​​ion

    collat​​ion属性指定排序规则URI,并标识用于比较字符串是否相等的排序规则。

    电流的基团()

    在xsl:for-each-group中,current-group()函数将当前正在处理的组作为一系列项目返回。

    电流分组键()

    在xsl:for-each-group中,current-group()函数返回当前组密钥。我不确定,但我相信这只能是一种原子类型。也不确定,但我相信这个功能仅适用于@ group-by和@ group-adjacent类型的分组。

    @ group-by与@ group-adjacent

    在某些情况下,您可以选择具有相同功能结果的这两种排序类型。在这种情况下,@ group-adjacent优先于@ group-by,因为它的处理效率可能更高。

    模式与选择

    某些XSLT 2.0指令属性包含select表达式。 Michael Kay称这些&#34; XPath表达式为#34;。就个人而言,当与图案并列时,我觉得更好的描述是&#34;选择表达&#34;。其他属性包含模式或&#34;匹配表达式&#34;。虽然这两个都包含相同的语法,但它们是非常不同的野兽。两者之间的相似性经常使XSLT初学者认为xsl:template / @匹配不是作为模式,而是作为选择表达式。结果是初学者对模板序列构造函数中position()函数的价值有很多困惑。如前所述,在xsl:for-each-group中,@ select,@ group-by和@ group-adjacent是select表达式,但@ group-starting-with和@ group-ending-with是模式。所以这就是区别:

    1. 选择表达式就像一个函数。输入是上下文文档,上下文序列,上下文项,上下文位置,当然还有实际表达式。输出是一系列项目。根据实际使用的位置,这可能成为下一个上下文序列。默认轴是child ::。
    2. 与select表达式不同,模式的默认轴是self ::。模式也像一个函数。它的输入和以前一样,它的输出不是序列,而是布尔值。正在测试某个项目以查看它是否与模式匹配。正在测试的项目是上下文项目。临时评估匹配表达式,因为它是一个选择表达式。然后测试返回的序列以查看上下文项是否是成员。然后丢弃返回的序列。结果是真的或匹配&#39;如果是会员,否则为假。

答案 1 :(得分:2)

Sean提供了一个非常慷慨的xsl:for-each-group的精彩概述,但它似乎并不是你问题的答案。

您已经展示了XSLT代码的一个片段,并且您已经说过想要更快的性能。但是你展示的片段并没有进行分组,它正在进行连接。有两种方法可以加快加入速度。要么使用Saxon-EE等XSLT处理器进行自动连接优化,要么使用密钥手动优化。例如,给出这个表达式:

/wo:document/styles [@wo:name=current()/@wo:style-name]/@wo:width

你可以定义一个键

<xsl:key name="style-name-key" match="styles" use="@wo:name"/>

然后用

替换表达式
key('style-name-key', @wo:style-name)/@wo:width