2012-06-20 17 views
6

Eksperymentuję z problemem z operatorem < na ciągach w Xpath 1.0.Jak porównać ciągi z Xpath 1.0?

Ten prosty Xpath wyrażenie

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

nie oceniać true w moim biegu XSLT na libxslt (który jest XSLT 1.0 silnik).

Sprawdziłem Szpieg XML, który pozwala na testowanie wyrażeń Xpath zarówno w wersji 1.0, jak i 2.0, i na pewno w Xpath 2.0 ocenia się na true, ale w Xpath 1.0 wartość ta wynosi false!

Czy to błąd w Xpath 1.0?

Jakie inne wyrażenie należy użyć do porównania dwóch ciągów/znaków w kolejności alfabetycznej? Zauważ, że funkcja compare() nie działa, ponieważ jest to funkcja XSLT 2.0.

Odpowiedz

4

Tak, jest to ograniczenie XPath 1.0. (Nie sądzę, żeby rozsądnie było odnosić się do ograniczenia, którego nie lubisz jako "błędu", chociaż wyraźnie projektanci XPath 2.0 zgodzili się z tobą, że było to niepożądane ograniczenie).

otagowaniu pytanie „XSLT”, więc może być w stanie obejść ten problem na poziomie XSLT, przynajmniej jeśli twój procesor ma węzła zestaw rozszerzeń:

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

ale może czas przejść na 2.0. Co cię powstrzymuje?

+0

Dzięki Michael - ładne kompaktowe rozwiązanie. Co do XSLT 2.0, co mnie powstrzymuje - 'libxslt' to - jest to silnik używany przez' php 5' i nie mogę tego zmienić. Być może w przyszłości mój serwer hostingowy użyje wersji php, która używa silnika XSLT 2.0 - kiedy taki istnieje. Naprawdę chciałbym robić to wszystko w XSLT 2.0 oczywiście - faktycznie zrobiłem to dla rozwoju, a potem musiałem wszystko przepisać. Zakładam ten sam powód, dla którego nie przechodzę do XSLT 2.0 dla dużej liczby programistów XSLT. – Maestro13

+0

@ Maestro13: Zobra obsługuje XPath 2.0 i jest dostępny jako rozszerzenie PHP, patrz: http://www.ibm.com/developerworks/xml/library/x-zorba/index.html - Jeśli chodzi o PHP, możesz również [zarejestruj funkcje PHP] (http://php.net/manual/en/domxpath.registerphpfunctions.php) jak 'strcmp' do użycia z twoim xpath. – hakre

+0

@hakre dzięki za informację - Sprawdzę z moim dostawcą usług hostingowych, czy mogą aktywować Zobra. A tak, alternatywą byłoby zarejestrowanie niestandardowej funkcji php i użycie jej w xslt - w ogniu walki XSLT zupełnie o tym zapomniałem. – Maestro13

1

To może być brzydkie rozwiązanie, a nie możliwe do zrealizowania w wielu sytuacjach, ale do prostego porównania alfabetycznego można użyć numeru translate. Poniższy urywek jest tylko przykładem, który może zostać przedłużony furtherly:

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

Twój przetłumaczyć wyraz powinna obejmować wszystkie litery, i niskie przypadki, i może być łatwo ponownie wykorzystane przez zdefiniowanie nazwie szablonu.

+0

tak to błąd i to jest obejście? ugh! – Maestro13

+0

Tylko limit. Spójrz na http://www.xsltfunctions.com/xsl/c0008.html#c0017 –

+0

Myślę, że to podejście stawia "D" przed "AA" (4 <11) –

7

W XPath 1.0 porównanie łańcuchów jest zdefiniowane tylko dla = i !=, a porównywanie zamówień nie jest dostępne. Spec mówi

Jeśli żaden obiekt porównywane jest zestaw węzłów i operator < =, <,> i> i przedmioty są porównywane poprzez przekształcenie zarówno obiektów ilościach i porównując liczb zgodnie z IEEE 754.

W ten sposób oba operandy są konwertowane na wartości zmiennoprzecinkowe, co powoduje, że oba są NaN.

Wierzę, że XML Microsoftu dodaje funkcje rozszerzające do obsługi tego, ale oczywiście to pomaga tylko jeśli używasz MSXML.

2

W nadziei, że okaże się to przydatne również dla innych, poniżej znajduje się kod napisany zgodnie z sugestią Michaela Kaya. Napisałem niestandardową funkcję compare, która daje takie same wyniki, jak w Xpath 2.0. Dodałem również tag php do pytania, aby można było go znaleźć częściej.

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

Wynikiem działania tego (z wejściem manekina) jest

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

Dla tych, którzy chcą przetestować to w php dla siebie, tutaj jest kod użyłem:

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