如何加快XSLT处理时间

Dav*_*866 1 xml xslt

我目前正在使用XSLT文件将XML文件(许多标记语言)传输到另一个XML文件(纯文本).处理时间太长.

我想这是因为我正在使用像这样的for-each内部for-each: 元素和属性

<xsl:for-each select="/data/row">
  <xsl:variable name="ROW_">
    <xsl:value-of select="count(./preceding-sibling::*) + 1"/>
  </xsl:variable>
  <xsl:for-each select='/Header/*[starts-with (text(), 'Car')] '>
    <xsl:variable name="COLUMN_">
      <xsl:value-of select="count(./preceding-sibling::*) + 1"/>
    </xsl:variable>
    <xsl:value-of select="/data/row[position()=$ROW_]/@*[position()=$COLUMN_]"/>
    <xsl:value-of select ="$Delimiter"/>
  </xsl:for-each>
</xsl:for-each>
Run Code Online (Sandbox Code Playgroud)

那么我该怎么做才能缩短处理时间呢?

Mic*_*Kay 7

这段代码中存在许多低效率,我将从小代码开始.

  <xsl:variable name="ROW_">
    <xsl:value-of select="count(./preceding-sibling::*) + 1"/>
  </xsl:variable>
Run Code Online (Sandbox Code Playgroud)

除非您真的想要创建临时XML树结构,否则切勿使用包含xsl:value-of的xsl:变量.写作效率更高

<xsl:variable name="ROW_" select="count(./preceding-sibling::*) + 1"/>
Run Code Online (Sandbox Code Playgroud)

按照自己的方式进行操作,您将计算整数值(使用count()),将其转换为字符串,将字符串转换为文本节点,创建文档节点以及将文本节点附加到文档节点; 当您在谓词中使用变量时[position()=$ROW_],您将通过查找并连接其所有文本节点并将结果转换为整数来获取文档节点的字符串值.首先将变量绑定到整数更好!

<xsl:for-each select='/Header/*[starts-with (text(), 'Car')] '>
Run Code Online (Sandbox Code Playgroud)

这出现在外部xsl:for-each中,因此它重复执行,但结果/Header/*[starts-with (text(), 'Car')]每次都相同,它不依赖于循环中的任何内容.智能优化器会将表达式移出循环(这不是一件容易的事,因为它依赖于识别"/"每次都会选择相同的根节点,这是唯一的,因为外部for-each选择一个-document node set).而不是依赖优化器那么聪明,将表达式绑定/Header/*[starts-with (text(), 'Car')]到变量.

<xsl:value-of select="/data/row[position()=$ROW_]/@*[position()=$COLUMN_]"/>
Run Code Online (Sandbox Code Playgroud)

这可能是关键的:在处理所有行的循环中,你有一个处理所有行的循环,所以你立即获得了O(n ^ 2)性能:输入大小加倍,执行时间变长增加了四倍.

同样,像Saxon-EE中那样的智能优化器可能会对此进行排序(构造就像是SQL中的连接,并且连接的优化是一种成熟的艺术).但是如果你使用的是开源处理器,那么它的优化器可能并不那么聪明,所以你必须手工优化它,这一点都不难做到:它看起来好像在排你选择将是外部for-each当前正在处理的那个.

FOUR

@*[position()=$COLUMN_]
Run Code Online (Sandbox Code Playgroud)

你在这里遇到了问题,这不是性能问题.您依赖于以特定顺序交付的属性(与相应的HEADER元素的顺序相同),这是不安全的.你根本不能依赖属性的顺序,因此你将不得不在这里找到一些控制输出顺序的方法.

忽略这个问题,我认为你的代码简化为:

<xsl:variable name="headers" 
    select='/Header/*[starts-with (text(), 'Car')] '/>
<xsl:for-each select="/data/row">
  <xsl:variable name="thisRow" select="."/>
  <xsl:for-each select='$headers'>
    <xsl:variable name="COLUMN_" 
         select="count(./preceding-sibling::*) + 1"/>
    <xsl:value-of select="$thisRow/@*[position()=$COLUMN_]"/>
    <xsl:value-of select ="$Delimiter"/>
  </xsl:for-each>
</xsl:for-each>
Run Code Online (Sandbox Code Playgroud)

(变量名末尾的下划线有什么意义?那种不必要的混淆让我真的很烦恼...)