2012-06-20 17 views
6

Ich habe ein Problem mit dem <-Operator für Strings in Xpath 1.0.Wie Strings mit Xpath 1.0 zu vergleichen?

Diese einfache XPath-Ausdruck

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

nicht wahr in meinem Xslt Lauf in libxslt (die eine XSLT 1.0 Engine) hat zu bewerten.

Ich habe XML Spion eingecheckt, was das Testen von Xpath-Ausdrücken sowohl in 1.0 als auch in 2.0 ermöglicht, und in Xpath 2.0 wird true ausgewertet, aber in Xpath 1.0 wird false ausgewertet!

Ist das ein Fehler in Xpath 1.0?

Welchen anderen Ausdruck sollte ich verwenden, um zwei Zeichenfolgen/Zeichen für ihre alphabetische Reihenfolge zu vergleichen? Beachten Sie, dass die Funktion compare() nicht funktioniert, da dies eine XSLT 2.0-Funktion ist.

Antwort

4

Ja, dies ist eine Einschränkung von XPath 1.0. (Ich denke nicht, dass es sinnvoll ist, auf eine Einschränkung hinzuweisen, die Sie nicht als "Fehler" bezeichnen, obwohl die Entwickler von XPath 2.0 Ihnen eindeutig zugestimmt haben, dass dies eine unerwünschte Einschränkung darstellt).

Sie haben Ihre Frage „xslt“ markiert, so Sie in der Lage sein können, auf der XSLT-Ebene, um das Problem zu arbeiten, zumindest, wenn Ihr Prozessor die Knotenmenge Erweiterung hat:

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

Aber vielleicht Es ist Zeit, zu 2.0 zu wechseln. Was hält dich zurück?

+0

Danke Michael - schöne kompakte Lösung. Was XSLT 2.0 betrifft, was mich zurückhält - "libxslt" ist - das ist die Engine von 'php 5' und ich kann das nicht ändern. Vielleicht wird mein Hosting-Dienstleister in Zukunft eine PHP-Version verwenden, die eine XSLT 2.0-Engine verwendet - wenn es eine gibt. Ich hätte das natürlich gerne in XSLT 2.0 gemacht - eigentlich tat ich das für die Entwicklung und musste dann alles neu schreiben. Ich gehe davon aus, dass der gleiche Grund, warum ich nicht zu XSLT 2.0 wechselte, für eine große Anzahl von XSLT-Entwicklern gilt. – Maestro13

+0

@ Maestro13: Zobra unterstützt XPath 2.0 und ist als PHP-Erweiterung verfügbar, siehe: http://www.ibm.com/developerworks/xml/library/x-zorba/index.html - Was PHP betrifft, können Sie auch [PHP-Funktionen registrieren] (http://php.net/manual/de/domxpath.registerphpfunctions.php) wie 'strcmp', um mit Ihrem xpath verwendet zu werden. – hakre

+0

@hakre Danke für die Info - Ich werde mit meinem Hosting-Provider überprüfen, ob sie Zobra aktivieren können. Und eine Alternative wäre, eine benutzerdefinierte PHP-Funktion zu registrieren und diese im XSLT zu verwenden - in der Hitze des XSLT-Kampfes habe ich das total vergessen. – Maestro13

1

Es könnte hässliche Lösung sein, und in vielen Situationen nicht durchführbar, aber für einen einfachen alphabetischen Bestellungsvergleich können Sie translate verwenden. Der folgende Ausschnitt ist nur ein Beispiel, das furtherly erweitert werden kann:

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

Ihr Ausdruck übersetzen soll alle Buchstaben decken, und niedrige Fällen und bequem durch die Definition einer benannten Vorlage wiederverwendet werden können.

+0

so es ist ein Fehler und das ist ein Workaround? Pfui! – Maestro13

+0

Nur eine Grenze. Sehen Sie http://www.xsltfunctions.com/xsl/c0008.html#c0017 –

+0

Ich denke, dass dieser Ansatz setzt "D" vor "AA" (4 <11) –

7

In XPath 1.0 ist der Zeichenfolgenvergleich nur für = und != definiert, und Bestellvergleiche sind nicht verfügbar. Die Spezifikation sagt

Wenn weder Objekt verglichen werden soll, ein Knotenmenge und der Bediener ist < =, <,> = oder>, dann werden die Objekte verglichen, indem beide Objekte Zahlen umzuwandeln und den Vergleich der Zahlen nach IEEE 754.

Somit werden beide Operanden in Float konvertiert, wodurch beide zu NaN werden.

Ich glaube, Microsoft XML fügt Erweiterungsfunktionen hinzu, aber das hilft natürlich nur, wenn Sie MSXML verwenden.

2

In der Hoffnung, dass dies sich auch für andere als nützlich erweist, ist unten der Code, den ich nach dem Vorschlag von Michael Kay geschrieben habe. Ich schrieb eine benutzerdefinierte compare Funktion, die die gleichen Ergebnisse wie Xpath 2.0 gibt. Ich fügte auch das php Tag der Frage hinzu, damit es öfter gefunden wird.

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

Das Ergebnis dieses laufen (mit Dummy-Eingang)

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

Für diejenigen, die dies für sich in PHP testen möchte, ist, hier ist der Code, den ich verwendet:

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