7

<在 Xpath 1.0 中遇到了字符串运算符的问题。

这个简单的 Xpath 表达式

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

在我在 libxslt(这是一个 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 函数。

4

4 回答 4

7

在 XPath 1.0 中,字符串比较仅针对=and定义!=,排序比较不可用。规范说

当要比较的对象都不是节点集并且运算符是 <=、<、>= 或 > 时,则通过将两个对象都转换为数字并根据 IEEE 754 比较数字来比较对象。

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

我相信 Microsoft 的 XML 添加了扩展函数来处理这个问题,但当然这只有在您使用 MSXML 时才有帮助。

于 2012-06-20T19:39:24.527 回答
5

是的,这是 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 了。是什么阻碍了你?

于 2012-06-20T21:43:38.220 回答
2

希望这也证明对其他人有用,下面是我根据 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 );
?>
于 2012-06-21T06:38:50.110 回答
1

这可能是丑陋的解决方案,并且在许多情况下不可行,但是对于简单的字母顺序比较,您可以使用translate. 以下片段只是一个可以进一步扩展的示例:

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

您的翻译表达式应涵盖所有字母,大写和小写,并且可以通过定义命名模板方便地重用。

于 2012-06-20T19:11:46.380 回答