2012-06-20 137 views
6

Estoy experimentando un problema con el operador < en cadenas en Xpath 1.0.¿Cómo comparar cadenas con Xpath 1.0?

Esta expresión XPath simples

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

no evaluaron a verdad en mi carrera en XSLT libxslt (que es un motor XSLT 1.0).

Comprobé XML Spy, que permite probar expresiones de Xpath en 1.0 y 2.0, y efectivamente, en Xpath 2.0 se evalúa como true, pero en Xpath 1.0 se evalúa como false.

¿Este es un error en Xpath 1.0?

¿Qué otra expresión debo usar para comparar dos cadenas/caracteres para su orden alfabético? Tenga en cuenta que la función compare() no funcionará, ya que es una función XSLT 2.0.

Respuesta

4

Sí, esto es una limitación de XPath 1.0. (No creo que sea razonable referirse a una limitación que no te gusta como un "error", aunque es evidente que los diseñadores de XPath 2.0 estuvieron de acuerdo contigo en que era una limitación indeseable).

ha etiquetado su pregunta "XSLT", por lo que puede ser capaz de solucionar el problema en el nivel de XSLT, al menos si su procesador tiene el conjunto de nodos de extensión:

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

Pero quizás es hora de pasar a 2.0. ¿Qué te detiene?

+0

Gracias Michael - buena solución compacta. En cuanto a XSLT 2.0, lo que me está frenando es que '' libxslt' es el motor utilizado por 'php 5' y no puedo cambiar eso. Tal vez en el futuro, mi servidor de alojamiento utilizará una versión de php que utiliza un motor XSLT 2.0, cuando hay uno. Realmente me hubiera gustado hacer todo esto en XSLT 2.0 por supuesto, de hecho lo hice para el desarrollo y luego tuve que reescribir todo. Supongo que la misma razón para no pasar a XSLT 2.0 se mantiene para una gran cantidad de desarrolladores de XSLT. – Maestro13

+0

@ Maestro13: Zobra admite XPath 2.0 y está disponible como una extensión de PHP, consulte: http://www.ibm.com/developerworks/xml/library/x-zorba/index.html - Por lo que respecta a PHP, puede hacerlo también [registrar funciones de PHP] (http://php.net/manual/en/domxpath.registerphpfunctions.php) como 'strcmp' para ser utilizado con su xpath. – hakre

+0

@hakre gracias por la información. Consultaré con mi proveedor de servicios de alojamiento si pueden activar Zobra. Y sí, una alternativa sería registrar una función de php personalizada y usarla en xslt; en el fragor de la batalla XSLT, me olvidé por completo de eso. – Maestro13

1

Puede ser una solución fea, y no es factible en muchas situaciones, pero para una simple comparación de orden alfabético puede usar translate. El siguiente fragmento es sólo un ejemplo que puede ser extendido furtherly:

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

Su traducir la expresión debe cubrir todas las letras, y los casos de baja, y podría ser reutilizado convenientemente mediante la definición de una plantilla llamada.

+0

por lo es un error y esto es una solución temporal? ugh! – Maestro13

+0

Solo un límite. Mira http://www.xsltfunctions.com/xsl/c0008.html#c0017 –

+0

Creo que ese enfoque pone "D" antes que "AA" (4 <11) –

7

En XPath 1.0, la comparación de cadenas se define solo para = y !=, y las comparaciones de pedidos no están disponibles. La especificación dice

Cuando ni objeto a comparar es un conjunto de nodos y el operador es < =, <,> = o>, entonces los objetos se comparan mediante la conversión de ambos objetos a los números y comparando la números de acuerdo con IEEE 754.

Por lo tanto, sus dos operandos se están convirtiendo en flotantes, convirtiéndolos en ambos NaN.

Creo que el XML de Microsoft agrega funciones de extensión para manejar esto, pero por supuesto esto solo ayuda si está usando MSXML.

2

Con la esperanza de que esto también sea útil para otros, a continuación se muestra el código que escribí siguiendo la sugerencia de Michael Kay. Escribí una función personalizada compare que da los mismos resultados que la de Xpath 2.0. También agregué la etiqueta php a la pregunta para que se encuentre con más frecuencia.

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

El resultado de la ejecución de este (con entrada ficticio) es

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

Para aquellos que deseen poner a prueba esta en php por sí mismos, aquí está el código que he utilizado:

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