¿Hay una statement “if -then-else” en XPath?

Parece que con toda la rica cantidad de funciones en xpath puedes hacer un “si”. Sin embargo, mi motor sigue insistiendo en que “no existe tal función”, y casi no encuentro ninguna documentación en la web (encontré algunas fonts dudosas, pero la syntax que tenían no funcionaba)

Necesito eliminar ‘:’ del final de una cadena (si existe), así que quería hacer esto:

if (fn:ends-with(//div [@id='head']/text(),': ')) then (fn:substring-before(//div [@id='head']/text(),': ') ) else (//div [@id='head']/text()) 

¿Algún consejo?

Sí, hay una forma de hacerlo en XPath 1.0:

 concat (
   subcadena ($ s1, 1, número ($ condición) * cadena-longitud ($ s1)),
   subcadena ($ s2, 1, número (no ($ condición)) * cadena-longitud ($ s2))
 )

Esto se basa en la concatenación de dos cadenas mutuamente excluyentes, la primera está vacía si la condición es falsa ( 0 * string-length(...) ), la segunda está vacía si la condición es verdadera. Esto se llama “método de Becker” , atribuido a Oliver Becker .

En tu caso:

 concat (
   subcadena (
     substring-before (// div [@ id = 'head'] / text (), ':'),
     1, 
     número(
       ends-with (// div [@ id = 'head'] / text (), ':')
     )
     * string-length (substring-before (// div [@ id = 'head'] / text (), ':'))
   ),
   subcadena (
     // div [@ id = 'head'] / text (), 
     1, 
     número (no
       ends-with (// div [@ id = 'head'] / text (), ':')
     )
     * string-length (// div [@ id = 'head'] / text ())
   )
 )

Aunque trataría de deshacerme de todo el "//" antes.

Además, existe la posibilidad de que //div[@id='head'] devuelva más de un nodo.
Solo ten en cuenta que usar //div[@id='head'][1] es más defensivo.

La especificación del lenguaje oficial para XPath 2.0 en W3.org detalla que el lenguaje sí admite instrucciones if . Ver la Sección 3.8 Expresiones condicionales , en particular. Junto con el formato de syntax y la explicación, da el siguiente ejemplo:

 if ($widget1/unit-cost < $widget2/unit-cost) then $widget1 else $widget2 

Esto sugeriría que no debería tener corchetes alrededor de sus expresiones (de lo contrario, la syntax parece correcta). No estoy totalmente seguro, pero seguramente vale la pena intentarlo. Por lo tanto, querrá cambiar su consulta para que se vea así:

 if (fn:ends-with(//div [@id='head']/text(),': ')) then fn:substring-before(//div [@id='head']/text(),': ') else //div [@id='head']/text() 

Sospecho fuertemente que esto puede solucionarlo, sin embargo, como el hecho de que su motor XPath parece estar tratando de interpretarlo como una función, donde de hecho es una construcción especial del lenguaje.

Finalmente, para señalar lo obvio, asegúrese de que su motor XPath sea compatible con XPath 2.0 (a diferencia de una versión anterior). No creo que las expresiones condicionales sean parte de versiones anteriores de XPath.

Personalmente, usaría XSLT para transformar el XML y eliminar los dos puntos. Por ejemplo, supongamos que tengo esta entrada:

   This paragraph ends in a period. This one ends in a colon: This one has a : in the middle.  

Si quisiera quitar dos puntos en mis párrafos, usaría este XSLT:

                           

de acuerdo con la ley de pkarat, puede lograr XPath condicional en la versión 1.0.

Para su caso, siga el concepto:

 concat(substring-before(your-xpath[contains(.,':')],':'),your-xpath[not(contains(.,':'))]) 

Esto definitivamente funcionará. Vea cómo funciona. Dar dos entradas

 praba: karan 

Para la primera entrada: contiene : así que condición verdadera, cadena antes : será la salida, por ejemplo, praba es tu salida. La 2da condición será falsa, así que no hay problemas.

Para la 2da entrada: no contiene : entonces la condición falla, llegando a la 2da condición que la cadena no contiene : así que condición verdadera … por lo tanto se arrojará la salida karan .

Finalmente tu salida sería praba , karan .

¿Qué tal si usamos fn: replace (string, pattern, replace) en su lugar?

XPATH se usa muy a menudo en XSLT y si se encuentra en esa situación y no tiene XPATH 2.0, puede usar:

    condition1-statements   condition2-statements   otherwise-statements   

Lamentablemente, las respuestas anteriores no eran una opción para mí, así que investigué por un tiempo y encontré esta solución:

http://blog.alessio.marchetti.name/post/2011/02/12/the-Oliver-Becker-s-XPath-method

Lo uso para enviar texto si existe un Nodo determinado. 4 es la longitud del texto foo. Así que supongo que una solución más elegante sería el uso de una variable.

 substring('foo',number(not(normalize-space(/elements/the/element/)))*4) 

Una solución XPath 1.0 algo más simple, adaptada de Tomalek (publicado aquí) y de Dimitre ( aquí ):

 concat(substring($s1, 1 div number($cond)), substring($s2, 1 div number(not($cond)))) 

Nota: Encontré que se requería un número explícito () para convertir el bool a un int; de lo contrario, algunos evaluadores de XPath lanzaron un error de desajuste de tipo. Dependiendo de cuán estricto sea el tipo de coincidencia de tipos de tu procesador XPath, es posible que no lo necesites.