2012-06-20 69 views
6

我在Xpath 1.0中的字符串上遇到<运算符问题。如何比较字符串与Xpath 1.0?

这个简单的XPath表达式

'A' < 'B' (or the equivalent 'A' &lt; 'B') 

没有评估为真,在我的libxslt XSLT运行(这是一个XSLT 1.0引擎)。

我检查了XML Spy,它允许在1.0和2.0中测试Xpath表达式,当然,在Xpath 2.0中它的计算结果为true,但在Xpath 1.0中它的计算结果为false

这是Xpath 1.0中的错误吗?

我应该用什么其他表达式来比较两个字符串/字符的字母顺序?请注意,compare()函数不会执行,因为这是一个XSLT 2.0函数。

回答

4

是的,这是XPath 1.0的限制。 (我认为将你不喜欢的限制称为“bug”是不合理的,尽管XPath 2.0的设计者明确同意这是一个不受欢迎的限制)。

您已经标记问题“XSLT”,所以你可能能够解决此问题在XSLT水平,至少如果您的处理器具有节点集扩展:

<xsl:variable name="nodes"> 
    <node><xsl:value-of select="$A"/></node> 
    <node><xsl:value-of select="$B"/></node> 
</xsl:variable> 

<xsl:for-each select="exslt:node-set($nodes)/*"> 
    <xsl:sort select="."/> 
    <xsl:if test="position()=1 and .=$A">A comes first!</xsl:if> 
</xsl:for-each> 

但也许现在是时候转移到2.0。什么阻止你回来?

+0

谢谢迈克尔 - 不错的紧凑解决方案。至于XSLT 2.0,有什么让我反感 - 'libxslt'是 - 这是'php 5'使用的引擎,我无法改变它。也许在将来,我的托管服务商将使用一个使用XSLT 2.0引擎的PHP版本 - 如果有的话。当然,我真的很想在XSLT 2.0中做所有这些事情 - 实际上我是为了开发而做的,然后不得不重写所有内容。我认为,对于大量的XSLT开发人员来说,不移动到XSLT 2.0的原因是相同的。 – Maestro13

+0

@ Maestro13:Zobra支持XPath 2.0并可作为PHP扩展使用,请参阅:http://www.ibm.com/developerworks/xml/library/x-zorba/index.html - 就PHP而言,您可以[注册PHP函数](http://php.net/manual/en/domxpath.registerphpfunctions.php)像'strcmp'用于你的xpath。 – hakre

+0

@hakre谢谢你的信息 - 我会去检查我的托管服务提供商是否可以激活Zobra。是的,另一种方法是注册一个自定义php函数,并在xslt中使用它 - 在XSLT战斗中,我完全忘记了这一点。 – Maestro13

1

这可能是难看的解决方案,在许多情况下不可行,但对于简单的字母顺序比较,您可以使用translate。下面的代码片断只是一个可以furtherly扩展的例子:

translate('A','ABCD','1234') &lt; translate('B','ABCD','1234'); 

您的翻译表达应涵盖所有的信件,并低的情况下,并且可以通过定义命名模板可以方便地重复使用。

+0

所以这是一个错误,这是一个解决方法?啊! – Maestro13

+0

只是一个限制。看看http://www.xsltfunctions.com/xsl/c0008.html#c0017 –

+0

我认为这种方法把“D”放在“AA”之前(4 <11) –

7

在XPath 1.0中,只对=!=定义了字符串比较,并且不提供排序比较。该规范表示

当待比较既不对象是一个节点集合和操作员是 < =,<,> =或>,则目的是通过既 对象转换为数字和比较所述比较根据IEEE 754.

因此,您的操作数都被转换为浮点数,使它们都是NaN。

我相信微软的XML增加了扩展功能来处理这个问题,当然这只有在你使用MSXML的时候才有用。

2

为了证明这对其他人也有用,下面是我根据迈克尔凯的建议写的代码。我写了一个自定义的compare函数,它提供了与Xpath 2.0相同的结果。我还为该问题添加了php标签,以便更频繁地找到它。

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:func="http://exslt.org/functions" 
    xmlns:common="http://exslt.org/common" 
    xmlns:custom="urn:myCustomFunctions" 
    exclude-result-prefixes="func common custom" 
    extension-element-prefixes="func custom"> 

    <xsl:output method="xml"/> 

    <func:function name="custom:compare"> 
     <xsl:param name="string1"/> 
     <xsl:param name="string2"/> 

     <func:result> 
      <xsl:choose> 
       <xsl:when test="$string1 = $string2">0</xsl:when> 
       <xsl:otherwise> 
        <xsl:variable name="nodes"> 
         <node><xsl:value-of select="$string1"/></node> 
         <node><xsl:value-of select="$string2"/></node> 
        </xsl:variable> 
        <xsl:for-each select="common:node-set($nodes)/*"> 
         <xsl:sort select="."/> 
         <xsl:choose> 
          <xsl:when test="position()=1 and .=$string1">-1</xsl:when> 
          <xsl:when test="position()=1 and .=$string2">1</xsl:when> 
         </xsl:choose> 
        </xsl:for-each> 
       </xsl:otherwise> 
      </xsl:choose> 
     </func:result> 
    </func:function> 

    <xsl:template match="/"> 
     <out> 
      <test1><xsl:value-of select="custom:compare('A', 'B')"/></test1> 
      <test2><xsl:value-of select="custom:compare('A', 'A')"/></test2> 
      <test3><xsl:value-of select="custom:compare('C', 'B')"/></test3> 
      <test4><xsl:value-of select="custom:compare('DD', 'A')"/></test4> 
     </out> 
    </xsl:template> 

</xsl:stylesheet> 

运行这个(与虚拟输入)的结果是

<?xml version="1.0"?> 
<out> 
    <test1>-1</test1> 
    <test2>0</test2> 
    <test3>1</test3> 
    <test4>1</test4> 
</out> 

对于那些谁愿意来测试这个在PHP自己,这是我使用的代码:

<?php 
$xslt = new XSLTProcessor(); 
$xslt->importStylesheet(DOMDocument::load('testCompare.xslt')); 
$xslt -> registerPHPFunctions(); 
$xml = new SimpleXMLElement('<test/>'); 
print $xslt->transformToXML($xml); 
?>