1

作为 XSLT 的新手,我希望这是一个简单的问题。我有一些平面 XML 文件,它们可能非常大(例如 7MB),我需要使它们“更具层次性”。例如,平面 XML 可能如下所示:

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

它最终应该看起来像这样:

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

我有一个可以工作的 XSLT,它基本上获取所有 b 元素的节点集,然后使用“following-sibling”轴获取当前 b 节点之后的节点的节点集(即,following-sibling::* [位置()=$nodePos])。然后使用递归将兄弟姐妹添加到结果树中,直到找到另一个 b 元素(我当然已经对其进行了参数化,以使其更通用)。

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

问题是执行转换的时间随着 XML 文件的大小增加而无法接受。使用 XML Spy 对其进行调查,似乎是 'following-sibling' 和 'position()=' 在这两种方法中花费了时间。

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

谢谢,

麦克风

4

3 回答 3

1

是的,有一种方法可以更有效地做到这一点:参见Muenchian grouping。如果看过这个,您需要更多关于细节的帮助,请告诉我们。您需要的密钥类似于:

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

然后您可以遍历<b>元素,并为每个元素使用key('elements-by-group', generate-id())来获取紧随其后的元素<b>

“使 XML 更具层次性”的任务有时称为上转换,您的场景就是它的经典案例。您可能知道,XSLT 2.0 具有非常有用的分组特性,这些特性比 Muenchian 方法更易于使用。

在您的情况下,听起来您将使用<xsl:for-each-group group-starting-with="b" />or 来参数化元素名称,<xsl:for-each-group group-starting-with="*[local-name() = 'b']" />. 但也许您已经考虑过这一点,因此不能在您的环境中使用 XSLT 2.0。

更新:

为了响应参数化的请求,这里有一种无需密钥的方法。请注意,它可能会慢得多,具体取决于您的 XSLT 处理器。

<xsl:template match="D0011">
   <xsl:for-each select="*[local-name() = $sep]">
      <xsl:copy>
         <xsl:copy-of select="following-sibling::*[not(local-name() = $sep)
               and generate-id(preceding-sibling::*[local-name() = $sep][1]) =
                    generate-id(current())]" />
      </xsl:copy>
   </xsl:for-each>      
</xsl:template>

如评论中所述,您可以通过定义几个不同的键来保持键的性能优势,一个用于参数的每个可能值。然后,您使用 . 选择要使用的键<xsl:choose>

更新 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>
于 2011-01-10T16:10:46.010 回答
1
<xsl:key name="k1" match="D0011/*[not(self::b)]" use="generate-id(preceding-sibling::b[1])"/>

<xsl:template match="D0011">
  <xsl:copy>
    <xsl:apply-templates select="b"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="D0011/b">
  <xsl:copy>
    <xsl:copy-of select="key('k1', generate-id())"/>
  </xsl:copy>
</xsl:template>
于 2011-01-10T16:14:37.563 回答
0

这是细粒度的遍历模式:

<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>
于 2011-01-10T17:45:09.533 回答