Как сравнить строки с Xpath 1.0?

Я experiiencing проблема с < оператор на строках в Xpath 1.0.

Это простое выражение Xpath

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

не оценил true в моем XSLT-запуске в libxslt (который является движком XSLT 1.0).

Я проверил в XML Spy, который позволяет тестировать выражения Xpath как в 1.0, так и в 2.0, и, конечно же, в Xpath 2.0 он оценивает как true, но в Xpath 1.0 он оценивается как false!

это ошибка в Xpath 1.0?

какое другое выражение я должен использовать для сравнения двух строк / символов для их алфавитного порядка? Обратите внимание, что функция compare() не будет делать, так как это функция XSLT 2.0.

4 ответов


Да, это ограничение 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. Что держит тебя? назад?


в XPath 1.0 сравнение строк определяется только для = и !=, и сравнения заказа недоступны. Спецификация говорит

когда ни один из сравниваемых объектов не является набором узлов, а оператор = или >, затем объекты сравниваются путем преобразования обоих объекты для чисел и сравнения чисел в соответствии с IEEE 754.

таким образом, оба ваших операнда преобразуются в float, что делает их NaN.

I поверьте, XML Microsoft добавляет функции расширения для обработки этого, но, конечно, это помогает только при использовании MSXML.


в надежде, что это окажется полезным и для других, Ниже приведен код, который я написал после предложения Майкла Кея. Я написал 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') &lt; translate('B','ABCD','1234');

ваше выражение translate должно охватывать все буквы, верхние и нижние регистры и может быть удобно повторно использовано путем определения именованного шаблона.