7

<Xpath1.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が、Xpath1.0では評価されfalseます。

これはXpath1.0のバグですか?

2つの文字列/文字をアルファベット順に比較するには、他にどのような表現を使用する必要がありますか?これはXSLT2.0関数であるため、compare()関数は機能しないことに注意してください。

4

4 に答える 4

7

XPath 1.0では、文字列の比較はとに対してのみ定義されて=おり!=、順序の比較は使用できません。スペックによると

比較するオブジェクトがどちらもノードセットではなく、演算子が<=、<、> =、または>の場合、オブジェクトは両方のオブジェクトを数値に変換し、IEEE754に従って数値を比較することによって比較されます。

したがって、両方のオペランドがfloatに変換され、両方がNaNになります。

MicrosoftのXMLはこれを処理するための拡張機能を追加していると思いますが、もちろんこれはMSXMLを使用している場合にのみ役立ちます。

于 2012-06-20T19:39:24.527 に答える
5

はい、これはXPath1.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

これが他の人にも役立つことを願って、MichaelKayの提案に従って私が書いたコードを以下に示します。compareXpath2.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 に答える