Performance XSLT1.0:限制节点集中的条目

Performance XSLT1.0:限制节点集中的条目,performance,xslt,nodesets,Performance,Xslt,Nodesets,由于对XSLT比较陌生,我希望有一个简单的问题。我有一些平面XML文件,它可能相当大(例如7MB),我需要使其“更层次化”。例如,平面XML可能如下所示: <D0011> <b/> <c/> <d/> <e/> <b/> .... .... </D0011> <D0011> <b> <c/> <

由于对XSLT比较陌生,我希望有一个简单的问题。我有一些平面XML文件,它可能相当大(例如7MB),我需要使其“更层次化”。例如,平面XML可能如下所示:

<D0011>
    <b/>
    <c/>
    <d/>
    <e/>
    <b/>
    ....
    ....
</D0011>
<D0011>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
  <b>
 ....
 ....
</D0011>

....
....
结果应该是这样的:

<D0011>
    <b/>
    <c/>
    <d/>
    <e/>
    <b/>
    ....
    ....
</D0011>
<D0011>
  <b>
    <c/>
    <d/>
    <e/>
  </b>
  <b>
 ....
 ....
</D0011>

....
....
我有一个可用于此目的的XSLT,它基本上获取所有b元素的节点集,然后使用“跟随同级”轴获取当前b节点之后的节点集(即跟随同级::*[position()=$nodePos])。然后使用递归将兄弟元素添加到结果树中,直到找到另一个b元素(当然,我已经对其进行了参数化,使其更通用)

我还有一个解决方案,它只发送下一个b节点在XML中的位置,并通过*[position()=$nodePos]选择一个接一个地选择该节点之后的节点(使用递归)

问题是,执行转换的时间随着XML文件的大小而不可接受地增加。通过XMLSpy查看,似乎是“following sibling”和“position()=”在这两个各自的方法中占用了时间

我真正需要的是一种限制上述选择中节点数量的方法,以便执行更少的比较:每次测试位置时,都会测试节点集中的每个节点,以查看其位置是否正确。有办法吗?还有其他建议吗

谢谢


迈克

是的,有一种方法可以更有效地做到这一点:参见。如果您已经了解了这一点,您需要更多关于细节的帮助,请告诉我们。您需要的密钥如下:

<xsl:key name="elements-by-group" match="*[not(self::b)]"
   use="generate-id(preceding-sibling::b[1])" />
如注释中所述,您可以通过定义几个不同的键来保持键的性能优势,每个键对应一个参数的可能值。然后使用
选择要使用的键

更新2:

要使组起始元素基于
/*/*[2]
定义,而不是基于参数,请使用

<xsl:key name="elements-by-group"
   match="*[not(local-name(.) = local-name(/*/*[2]))]"
   use="generate-id(preceding-sibling::*
                           [local-name(.) = local-name(/*/*[2])][1])" />

<xsl:template match="D0011">
   <xsl:for-each select="*[local-name(.) = local-name(../*[2])]">
      <xsl:copy>
         <xsl:copy-of select="key('elements-by-group', generate-id())"/>
      </xsl:copy>
   </xsl:for-each>
</xsl:template>


这是细粒度的trasversal模式:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()[1]|@*"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="b[1]" name="group">
        <xsl:copy>
            <xsl:apply-templates select="following-sibling::node()[1]"/>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::b[1]" mode="group"/>
    </xsl:template>
    <xsl:template match="b[position()!=1]"/>
    <xsl:template match="b" mode="group">
        <xsl:call-template name="group"/>
    </xsl:template>
</xsl:stylesheet>

输出:

<D0011>
    <b>
        <c></c>
        <d></d>
        <e></e>
    </b>
    <b>
    ....
    ....
    </b>
</D0011>

....
....

嗨,拉尔斯,非常感谢你的建议。我已经通读了关于门钦族的描述,它非常有趣。然而,我不太明白如何在我的情况下应用它!(我可能需要再次阅读XSLT书籍的第6章…)。其中有一句话“它可以应用于根据节点的属性(可通过XPath检索)对节点进行分组的任何情况。”-我的节点都处于同一级别,因此没有任何可以应用密钥的内容?顺便说一句,你说得对——我必须使用XSLT1.0。干杯,尼克,谢谢你的编辑!我会尝试一下,然后带着我期待的更愚蠢的问题回来……@LarsH:我不认为Muenchian方法在这里有什么用,因为我们不会选择“第一种”,这也是每个组的特殊属性
xsl:for each group/@group以
开头和
xsl:for each group/@group以
结尾如果你真的必须用XSLT 1.0解决这个问题,那么我很同情你(2.0让它更容易),但你有两种方法,这两种方法都是本论坛上的答案:(a)“兄弟递归”(通过以下同级轴递归)和(b)使用“generate id(preference sibling::b[1])作为分组键的Muenchian分组。Martin和Lars-性能上的卓越改进:至少快了10倍!但我昨晚读到match和use属性不能是变量(这显然在XSLT 1.0规范中)-灾难!为了避免有十个几乎相同的XSLT,再加上选择正确的XSLT所需的大量逻辑,我需要能够指定一个参数来替换“b”。有什么想法吗?干杯。谢谢你的建议-我会尝试一下,可能还会问一些更愚蠢的问题!如果我知道“b”元素(即,位于顶层的节点)始终是第二个节点,我可以在匹配中使用该节点并使用表达式吗?我尝试过“D0011/*[not(self::*['/*/*[2]')”(丑陋,我知道,但必须!)但它不起作用-可能是因为返回了一个节点,而不是“节点类型”。干杯,再次感谢您的帮助-您和Lars真的让我走上了正确的道路。