XPath contiene (texto (), 'alguna cadena') no funciona cuando se usa con un nodo con más de un subnodo de texto

259

Tengo un pequeño problema con Xpath contiene dom4j ...

Digamos que mi XML es

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

Digamos que quiero encontrar todos los nodos que tienen ABC en el texto dado el Elemento raíz ...

Entonces, el xpath que necesitaría escribir sería

//*[contains(text(),'ABC')]

Sin embargo, esto no es lo que devuelve Dom4j ... es un problema de dom4j o mi comprensión de cómo funciona xpath. dado que esa consulta solo devuelve el elemento Street y no el elemento Comment.

El DOM hace que el elemento Comentario sea un elemento compuesto con cuatro etiquetas dos

[Text = 'XYZ'][BR][BR][Text = 'ABC'] 

Supongo que la consulta aún debería devolver el elemento, ya que debería encontrar el elemento y ejecutarlo, pero no ...

la siguiente consulta devuelve el elemento pero devuelve mucho más que solo el elemento, también devuelve los elementos principales ... lo que no es deseable para el problema ...

//*[contains(text(),'ABC')]

¿Alguien sabe la consulta xpath que devolvería solo los elementos <Street/>y <Comment/>?

Mike Milkin
fuente
Por lo que puedo decir, //*[contains(text(),'ABC')]solo devuelve el <Street>elemento. No devuelve ningún antepasado de <Street>o <Comment>.
Ken Bloom

Respuestas:

707

La <Comment>etiqueta contiene dos nodos de texto y dos <br>nodos como elementos secundarios.

Tu expresión xpath fue

//*[contains(text(),'ABC')]

Para romper esto,

  1. * es un selector que coincide con cualquier elemento (es decir, etiqueta); devuelve un conjunto de nodos.
  2. El []son un condicional que opera en cada nodo individual en ese conjunto de nodos. Coincide si alguno de los nodos individuales en los que opera coincide con las condiciones dentro de los corchetes.
  3. text()es un selector que coincide con todos los nodos de texto que son hijos del nodo de contexto; devuelve un conjunto de nodos.
  4. containses una función que opera en una cadena. Si se pasa un conjunto de nodos, el conjunto de nodos se convierte en una cadena al devolver el valor de cadena del nodo en el conjunto de nodos que está primero en el orden del documento . Por lo tanto, solo puede coincidir con el primer nodo de texto en su <Comment>elemento, es decir BLAH BLAH BLAH. Como eso no coincide, no obtienes un <Comment>resultado en tus resultados.

Necesitas cambiar esto a

//*[text()[contains(.,'ABC')]]
  1. * es un selector que coincide con cualquier elemento (es decir, etiqueta); devuelve un conjunto de nodos.
  2. Los externos []son un condicional que opera en cada nodo individual en ese conjunto de nodos; aquí opera en cada elemento del documento.
  3. text()es un selector que coincide con todos los nodos de texto que son hijos del nodo de contexto; devuelve un conjunto de nodos.
  4. Los internos []son un condicional que opera en cada nodo en ese conjunto de nodos, aquí cada nodo de texto individual. Cada nodo de texto individual es el punto de partida para cualquier ruta entre paréntesis, y también se puede mencionar explícitamente como .dentro de los paréntesis. Coincide si alguno de los nodos individuales en los que opera coincide con las condiciones dentro de los corchetes.
  5. containses una función que opera en una cadena. Aquí se pasa un nodo de texto individual ( .). Como se pasa el segundo nodo de texto en la <Comment>etiqueta individualmente, verá la 'ABC'cadena y podrá hacerla coincidir.
Ken Bloom
fuente
1
Impresionante, soy un poco un novato de xpath, así que déjame entender esto, text () es una función que toma la expresión contiene (., 'ABC'). ¿Hay alguna posibilidad de que puedas explicarme? cosas estúpidas de nuevo;)
Mike Milkin
28
He editado mi respuesta para proporcionar una explicación larga. Realmente no sé mucho sobre XPath, solo experimenté un poco hasta que me topé con esa combinación. Una vez que tuve una combinación de trabajo, adiviné lo que estaba sucediendo y busqué en el estándar XPath para confirmar lo que pensaba que estaba sucediendo y escribir la explicación.
Ken Bloom
2
¿Cómo harías de esto una búsqueda insensible a mayúsculas y minúsculas?
Zack
@Zack: Por favor, haz una nueva pregunta.
user1129682
1
Sé que este es un hilo viejo, pero ¿alguien puede comentar si hay una diferencia fundamental, preferiblemente con algunos casos de prueba simples entre la respuesta dada por Ken Bloom y //*[contains(., 'ABC')]. Siempre había usado el patrón dado por Mike Milkin, pensando que era más apropiado, pero hacer containslo que quiero en el contexto actual parece ser lo que quiero con más frecuencia.
knickum
7

[contains(text(),'')]solo devuelve verdadero o falso. No devolverá ningún resultado de elemento.

Ratna
fuente
esto no funcionaría si tuviera '' o '' ¿cómo podemos recortar?
shareef
contains(text(),'JB-')no es trabajo! conatainstoma dos cadenas como argumentos - contains(**string**, **string**)! ¡text () no es una cadena , es una función!
AtachiShadow
6

El documento XML:

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

La expresión XPath:

//*[contains(text(), 'ABC')]

//*coincide con cualquier elemento descendiente del nodo raíz . Es decir, cualquier elemento menos el nodo raíz.

[...]es un predicado , filtra el conjunto de nodos. Devuelve nodos para los cuales ...es true:

Un predicado filtra un conjunto de nodos [...] para producir un nuevo conjunto de nodos. Para cada nodo en el conjunto de nodos a filtrar, se evalúa el PredicateExpr [...]; si PredicateExpr se evalúa como verdadero para ese nodo, el nodo se incluye en el nuevo conjunto de nodos; de lo contrario, no está incluido.

contains('haystack', 'needle')devuelve truesi haystack contiene needle :

Función: boolean contiene (cadena, cadena)

La función contiene devuelve verdadero si la primera cadena de argumento contiene la segunda cadena de argumento y, de lo contrario, devuelve falso.

Pero contains()toma una cadena como primer parámetro. Y ha pasado nodos. Para lidiar con eso, cada nodo o conjunto de nodos pasado como primer parámetro se convierte en una cadena por la string()función:

Un argumento se convierte para escribir una cadena como llamando a la función de cadena.

string()función devuelve string-valuedel primer nodo :

Un conjunto de nodos se convierte en una cadena al devolver el valor de cadena del nodo en el conjunto de nodos que está primero en el orden del documento. Si el conjunto de nodos está vacío, se devuelve una cadena vacía.

string-valuede un nodo de elemento :

El valor de cadena de un nodo de elemento es la concatenación de los valores de cadena de todos los descendientes de nodo de texto del nodo de elemento en orden de documento.

string-valuede un nodo de texto :

El valor de cadena de un nodo de texto son los datos de caracteres.

Entonces, básicamente string-valuetodo el texto está contenido en un nodo (concatenación de todos los nodos de texto descendientes).

text() es una prueba de nodo que coincide con cualquier nodo de texto:

El texto de prueba de nodo () es verdadero para cualquier nodo de texto. Por ejemplo, child :: text () seleccionará los hijos del nodo de texto del nodo de contexto.

Dicho esto, //*[contains(text(), 'ABC')]coincide con cualquier elemento (excepto el nodo raíz), cuyo primer nodo de texto contiene ABC. Dado que text()devuelve un conjunto de nodos que contiene todos los nodos de texto secundarios del nodo de contexto (en relación con el cual se evalúa una expresión). Pero contains()solo toma el primero. Entonces, para el documento de arriba, la ruta coincide con el Streetelemento.

La siguiente expresión //*[text()[contains(., 'ABC')]]coincide con cualquier elemento (pero el nodo raíz), que tiene al menos un nodo de texto secundario, que contiene ABC. .representa el nodo de contexto. En este caso, es un nodo de texto hijo de cualquier elemento que no sea el nodo raíz. Entonces, para el documento de arriba, la ruta coincide con Streetlos Commentelementos.

Ahora bien, //*[contains(., 'ABC')]coincide con cualquier elemento (pero el nodo raíz) que contiene ABC(en la concatenación de los nodos de texto descendientes). Para el documento anterior, coincide con los elementos Home, the Addr, the Streety the Comment. Como tal, //*[contains(., 'BLAH ABC')]coincide con los elementos Home, the Addry the Comment.

x-yuri
fuente
0

Me tomó un tiempo, pero finalmente lo descubrí. Xpath personalizado que contiene algo de texto a continuación funcionó perfectamente para mí.

//a[contains(text(),'JB-')]
zagoo2000
fuente
2
contains(text(),'JB-')no es trabajo! conatainstoma dos cadenas como argumentos - contains(**string**, **string**)! ¡text () no es una cadena , es una función!
AtachiShadow
0

La respuesta aceptada también devolverá todos los nodos principales. Para obtener solo los nodos reales con ABC incluso si la cadena es posterior
:

//*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]
Roger Veciana
fuente
0
//*[text()='ABC'] 

devoluciones

<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>
usuario3520544
fuente
3
Al agregar una respuesta a una pregunta de nueve años con cinco respuestas existentes, es muy importante señalar qué nuevo aspecto único de la pregunta aborda su respuesta.
Jason Aller
La respuesta que publiqué fue muy simple. Así que pensé en compartir, lo que puede ayudar a principiantes como yo.
user3520544