Как сравнить строки с Xpath 1.0?
Я experiiencing проблема с <
оператор на строках в Xpath 1.0.
Это простое выражение Xpath
'A' < 'B' (or the equivalent 'A' < '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') < translate('B','ABCD','1234');
ваше выражение translate должно охватывать все буквы, верхние и нижние регистры и может быть удобно повторно использовано путем определения именованного шаблона.