XSLT - 如何加速复杂的每个

时间:2012-08-15 10:52:14

标签: performance xslt optimization foreach xslt-2.0

我是XSLT的新手,我在下面的for-each语句中遇到了一些速度问题。我希望有人可以给我一些关于如何优化这个的指示吗?

下面的for-each循环遍历大约4mb的XML。它正在测试以确保每个酒店节点都有描述和目的地。它还测试每个酒店的评级大于2但不是6. XML中评级的可能值为0,1,2,3,4,5或6.理想情况下我希望它只选择评级3,4或5而忽略其他人。

<for-each select="response/results/hotel[
    not(@description = '') and
    @rating &gt; '2' and
    not(@rating = '6') and
    not(@destination = '')              ]">
  <call-template name="hotelparams"/>
  <call-template name="upropdata"/>
  <call-template name="request"/>
  <call-template name="Newline"/>
</for-each>

作为请求,我添加了下面调用的模板。输出是创建制表符分隔的文本文件,然后在mySQL中导入。顺便说一句,请忽略upropdata模板,它将很快被删除......

<xsl:template name="hotelparams">
<xsl:value-of select="@itemcode"/><xsl:value-of select="$tab"/>
<xsl:value-of  select="@cheapestcurrency"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@cheapestprice"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@checkin"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@checkout"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@description"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@destair"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@destination"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@destinationid"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@engine"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@hotelname"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@image"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@nights"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@rating"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@resultkey"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@resultno"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@supplierdestination"/><xsl:value-of select="$tab"/>
<xsl:value-of select="@type"/></xsl:template>

<xsl:template name="upropdata">
<xsl:value-of select="$tab"/>\N<xsl:value-of select="$tab"/>\N<xsl:value-of select="$tab"/>\N<xsl:value-of select="$tab"/>\N<xsl:value-of select="$tab"/>\N<xsl:value-of select="$tab"/>2011-01-01</xsl:template>

<xsl:template name="request">
<xsl:for-each select="/response/request/method"><xsl:value-of select="$tab"/><xsl:value-of select="./@sessionkey"/></xsl:for-each></xsl:template>
<xsl:template name="Newline">
<xsl:text>&#13;</xsl:text></xsl:template>

2 个答案:

答案 0 :(得分:0)

怎么样......

<xsl:for-each select="response/results/hotel
      [not(@description = '')]
      [@rating = (3,4,5)]">
  <xsl:call-template name="hotelparams"/>
  <xsl:call-template name="upropdata"/>
  <xsl:call-template name="request"/>
  <xsl:call-template name="Newline"/>
</xsl:for-each>

注意:我没有包含目的地检查,因为您没有指定其节点名称。

另外,如果你可以消除空描述属性的可能性(也就是说酒店将有非空描述或根本没有描述属性),那么你可以使用这个稍微缩写的形式......

<xsl:for-each select="response/results/hotel
      [not(@description)]
      [@rating = (3,4,5)]">
  <xsl:call-template name="hotelparams"/>
  etc...
</xsl:for-each>

另请注意,第二个谓词的替代形式是......

      [@rating = (3 to 5)]

有人可以写......

      [(@rating &gt; 2) and (@rating &lt; 6)]

      [@rating &gt; 2][@rating &lt; 6]

...但我怀疑这样效率会降低,因为@rating必须被提取两次。

答案 1 :(得分:0)

  

下面的for-each循环遍历大约4mb的XML。正在测试   确保每个酒店节点都有描述和目的地。它   还测试每家酒店的评级大于2但不是6。   XML中评级的可能值为0,1,2,3,4,5或   6.理想情况下,我希望它只选择等级3,4或5而忽略其他等级。

<for-each select="response/results/hotel[
    not(@description = '') and
    @rating &gt; '2' and
    not(@rating = '6') and
    not(@destination = '')              ]">
  <call-template name="hotelparams"/>
  <call-template name="upropdata"/>
  <call-template name="request"/>
  <call-template name="Newline"/>
</for-each>

我认为性能问题的原因在于被调用的模板(并未在问题中提供) - 而不是xsl:for-each本身。

它可以用不同的替代方式重写,但性能提升最小(毫秒),如果有的话。

请注意,提供的代码不会检查是否存在@destination属性。任何满足其他条件但没有hotel属性destination元素都会被选中。

description属性完全相同。

指定xsl:for-each的一种正确方法是

<xsl:for-each select="response/results/hotel[
     string(@description)
    and
     @rating > 2
    and
     not(@rating > 5)
    and
     string(@destination)
    ]">
  <xsl:call-template name="hotelparams"/>
  <xsl:call-template name="upropdata"/>
  <xsl:call-template name="request"/>
  <xsl:call-template name="Newline"/>
</xsl:for-each>

<强>更新

OP现在提供了被调用模板的代码。

我将对hotelparams模板使用以下内容:

<xsl:sequence select=
 "string-join
  (
   (@itemcode,
    @cheapestcurrency,
    @cheapestprice,
    @checkin,
    @checkout,
    @description,
    @destair,
    @destination,
    @destinationid,
    @engine,
    @hotelname,
    @image,
    @nights,
    @rating,
    @resultkey,
    @resultno,
    @supplierdestination,
    @type),
   $tab
  )
 "/>

我会将模板upropdata替换为:

此代码:

 <xsl:sequence select="'&#9;\N&#9;\N&#9;\N&#9;\N&#9;\N2011-01-01'"/>

或者,如果$tab确实可能与&#9;不同,我只计算一次并将结果放在一个全局变量中:

<xsl:variable name="vUPropData" select=
"concat($tab,'\N',$tab,'\N',$tab,'\N'$tab,'\N',$tab,'\N2011-01-01')"/>

然后只有:

 <xsl:sequence select="$vUPropData"/>

我会将request模板替换为

此代码:

<xsl:sequence select=
  "concat($tab,string-join(/response/request/method/@sessionkey, $tab))"/>

由于这不依赖于任何上下文节点(是一个绝对表达式),我只计算一次并将其放在一个全局变量中(如前面的情况),并且只引用这个全局变量。

最后,在命名模板中生成相同的单个字符没有意义。我将用全局变量或全局参数替换Newline模板。

我相信在重构之后,代码可能会执行得更快