How to compare strings with Xpath 1.0?

I am having a problem with the < operator for strings in Xpath 1.0.

This is a simple Xpath expression.

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

did not evaluate true in my xslt run in libxslt (which is the XSLT 1.0 engine).

I checked in XML Spy, which allows you to test Xpath expressions in versions 1.0 and 2.0, and of course in Xpath 2.0 it evaluates to true , but in Xpath 1.0 it evaluates to false !

Is this a bug in Xpath 1.0?

What other expression should be used to compare two strings / characters for their alphabetical order? Note that the compare () function will not work, as it is an XSLT 2.0 function.

+6
source share
4 answers

Yes, this is a limitation of XPath 1.0. (I donโ€™t think itโ€™s wise to refer to a restriction that you donโ€™t like as an โ€œerrorโ€, although XPath 2.0 developers agreed with you that this is an undesirable restriction).

You marked your question with "xslt" so that you can solve the problem at the XSLT level, at least if your processor has a node extension:

 <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> 

But maybe it's time to move on to 2.0. What is holding you back?

+4
source

In XPath 1.0, string comparisons are only defined for = and != , And comparison comparisons are not available. Spectrum says

If no object to be compared is a node-set, and the operator is <=, <,> = or>, then the objects are compared, converting both objects to numbers and comparing numbers in accordance with IEEE 754.

Thus, both operands are converted to float, making them like NaN.

I believe that Microsoft XML adds extension functions to handle this, but of course it only helps if you use MSXML.

+7
source

In the hope that this will prove useful to others, the code that I wrote after Michael Kayโ€™s suggestion is given below. I wrote a custom compare function that gives the same results as Xpath 2.0. I also added a php tag to the question so that it is found more often.

 <?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> 

The result of doing this (with dummy input) is

 <?xml version="1.0"?> <out> <test1>-1</test1> <test2>0</test2> <test3>1</test3> <test4>1</test4> </out> 

For those who want to test this in php for themselves, here is the code I used:

 <?php $xslt = new XSLTProcessor(); $xslt->importStylesheet( DOMDocument::load('testCompare.xslt') ); $xslt -> registerPHPFunctions(); $xml = new SimpleXMLElement('<test/>'); print $xslt->transformToXML( $xml ); ?> 
+2
source

It may be an ugly solution, and it is not possible in many situations, but you can use translate for simple comparisons in alphabetical order. The following snippet is just an example that can be continued:

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

Your translation expression should cover all letters, upper and lower regions, and they could be reused by defining a named pattern.

+1
source

Source: https://habr.com/ru/post/918588/


All Articles