2012-06-20 14 views
6

Sto riscontrando un problema con l'operatore < su stringhe in Xpath 1.0.Come confrontare le stringhe con Xpath 1.0?

Questa semplice espressione XPath

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

non ha valutato a vero nella mia corsa XSLT in libxslt (che è un XSLT 1.0 del motore).

ho controllato in XML Spy, che consente espressioni test Xpath sia in 1.0 e 2.0, e abbastanza sicuro, in XPath 2.0 si restituisce true, ma in XPath 1.0 si valuta a false!

Si tratta di un bug in Xpath 1.0?

Quale altra espressione dovrei usare per confrontare due stringhe/caratteri per il loro ordine alfabetico? Notare che la funzione compare() non funzionerà, poiché questa è una funzione XSLT 2.0.

risposta

4

Sì, questa è una limitazione di XPath 1.0. (Non penso sia ragionevole fare riferimento a una limitazione che non ti piace come un "bug", anche se chiaramente i progettisti di XPath 2.0 concordavano con te che si trattava di una limitazione indesiderabile).

Hai codificato tua domanda "XSLT", in modo da essere in grado di risolvere il problema a livello di XSLT, almeno se il processore è il nodo-set di estensione:

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

Ma forse è ora di passare alla 2.0. Cosa ti trattiene?

+0

Grazie Michael - bella soluzione compatta. Per quanto riguarda XSLT 2.0 ciò che mi trattiene - 'libxslt' è - questo è il motore usato da' php 5' e non posso cambiarlo. Forse in futuro il mio hosting servirà a utilizzare una versione php che utilizza un motore XSLT 2.0, quando ce n'è uno. Mi piacerebbe davvero fare tutto questo in XSLT 2.0 ovviamente - in effetti l'ho fatto per lo sviluppo e poi ho dovuto riscrivere tutto. Presumo la stessa ragione per non passare alle prese XSLT 2.0 per un gran numero di sviluppatori XSLT. – Maestro13

+0

@ Maestro13: Zobra supporta XPath 2.0 ed è disponibile come estensione PHP, vedere: http://www.ibm.com/developerworks/xml/library/x-zorba/index.html - Per quanto riguarda PHP è possibile anche [register PHP functions] (http://php.net/manual/en/domxpath.registerphpfunctions.php) come 'strcmp' da usare con xpath. – hakre

+0

@hakre grazie per le informazioni - controllerò con il mio fornitore di servizi di hosting se possono attivare Zobra. E sì un'alternativa sarebbe quella di registrare una funzione php personalizzata e usarla nella xslt - nel calore della battaglia XSLT l'ho completamente dimenticato. – Maestro13

1

Potrebbe essere una soluzione brutta e non fattibile in molte situazioni, ma per un confronto di ordine alfabetico semplice è possibile utilizzare translate. Il seguente frammento è solo un esempio che può essere esteso ulteriormente:

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

tuo tradurre espressione dovrebbe coprire tutte le lettere, e casi di bassi, e potrebbe essere facilmente riutilizzato definendo un modello di nome.

+0

così è un bug e questa è una soluzione? ugh! – Maestro13

+0

Solo un limite. Guarda http://www.xsltfunctions.com/xsl/c0008.html#c0017 –

+0

Penso che questo approccio metta "D" prima di "AA" (4 <11) –

7

In XPath 1.0, il confronto delle stringhe è definito solo per = e != e non sono disponibili i confronti di ordinazione. Le specifiche dice

Quando nessun oggetto da comparare è un set-nodo e l'operatore è < =, <,> = o>, quindi gli oggetti vengono confrontati convertendo entrambi oggetti da numeri e confrontando il numeri secondo IEEE 754.

Così entrambi gli operandi vengono convertiti in virgola mobile, rendendoli entrambi NaN.

Credo che Microsoft XML aggiunga funzioni di estensione per gestirlo, ma ovviamente questo aiuta solo se si utilizza MSXML.

2

Nella speranza che questo si riveli utile anche agli altri, di seguito è riportato il codice che ho scritto seguendo il suggerimento di Michael Kay. Ho scritto una funzione personalizzata compare che fornisce gli stessi risultati di quella di Xpath 2.0. Ho anche aggiunto il tag php alla domanda in modo che venga trovato più spesso.

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

Il risultato dell'esecuzione di questo (con ingresso fittizio) è

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

Per coloro che desiderano provare questo in php per se stessi, ecco il codice che ho usato:

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