我在 Xpath 1.0 中遇到字符串上的<
运算符问题。
这个简单的 Xpath 表达式
'A' < 'B' (or the equivalent 'A' < 'B')
在 libxslt 中的 XSLT(这是一个 XSLT 1.0 引擎)中,我的 XSLT 运行中没有评估为 true。
我检查了XML Spy,它允许在1.0和2.0中测试Xpath表达式,果然,在Xpath 2.0中,它的计算结果为true
,但在Xpath 1.0中,它的计算结果为false
!
这是 Xpath 1.0 中的错误吗?
我应该使用什么其他表达式来比较两个字符串/字符的字母顺序?请注意,compare() 函数不起作用,因为这是一个 XSLT 2.0 函数。
在 XPath 1.0 中,字符串比较只针对 =
和 !=
定义,并且排序比较不可用。 规范说
当要比较的对象都不是节点集并且运算符是 <=、<、>= 或>,则通过转换两者来比较对象 反对数字并根据 IEEE 754 比较数字。
因此,您的两个操作数都将转换为浮点数,使它们都是 NaN。
我相信Microsoft的XML添加了扩展函数来处理这个问题,但当然,这只有在您使用MSXML时才有帮助。
是的,这是 XPath 1.0 的限制。(我认为将您不喜欢的限制称为"错误"是不合理的,尽管显然 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 了。是什么阻碍了你?
希望这也能证明对其他人有用,下面是我根据Michael Kay的建议编写的代码。我编写了一个自定义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 );
?>
这可能是丑陋的解决方案,在许多情况下是不可行的,但对于简单的字母顺序比较,您可以使用translate
。以下代码片段只是一个可以进一步扩展的示例:
translate('A','ABCD','1234') < translate('B','ABCD','1234');
您的翻译表达式应涵盖所有字母,包括向上和低大小写,并且可以通过定义命名模板方便地重用。