2010-07-13 15 views
6

tengo un archivo XML como esto:sensitiva a la búsqueda de XPath en php

<volume name="Early"> 
<book name="School Years"> 
<chapter number="1"> 
<line number="1">Here's the first line with Chicago in it.</line> 
<line number="2">Here's a line that talks about Atlanta</line> 
<line number="3">Here's a line that says chicagogo </line> 
</chapter> 
</book> 
</volume> 

que estoy tratando de hacer una simple búsqueda por palabra clave usando PHP que encuentra la palabra y muestra la línea que estaba en lo que tengo. este trabajo

$xml = simplexml_load_file($data); 
$keyword = $_GET['keyword']; 
$kw=$xml->xpath("//line[contains(text(),'$keyword')]"); 
...snip... 

echo $kw[0]." is the first returned item"; 

sin embargo, el uso de esta técnica, un usuario obligada búsqueda de 'Chicago' y no 'Chicago', o la búsqueda devolverá nada.

Entiendo que necesito usar la función de traducción, pero toda mi prueba y error ha sido en vano.

que he probado:

$upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 
$lower = "abcdefghijklmnopqrstuvwxyz"; 
$kw = $xml->xpath("line[contains(text(),'translate('$keyword','$upper','$lower'))]"); 

pero nada parece funcionar. ¿algun consejo?

+0

Parece tener una cotización individual extra frente a 'translate' en su último ejemplo de código. – Charles

Respuesta

7

recomendación de Gordon utilizar una función de PHP desde XPath resultará más flexible en caso que quiera usar eso. Sin embargo, contrariamente a su respuesta, la función de cadena translatees disponible en XPath 1.0, lo que significa que puede usarla; su problema es cómo.

En primer lugar, está el error evidente que Charles señaló en su comentario a la pregunta. Luego está la lógica de cómo estás tratando de hacer coincidir los valores de texto.


En forma de la palabra, que está pidiendo ahora, "¿El texto contiene la forma minúscula de la palabra clave?" Esto no es realmente lo que quiere estar preguntando. En su lugar, pregunte, "qué el texto en minúsculas contienen la palabra clave en minúsculas?" Traslación (perdón por el juego de palabras) que de nuevo en XPath-tierra sería:

(Nota: alfabetos truncados para facilitar la lectura)

//line[contains(translate(text(),'ABC...Z','abc...z'),'chicago')] 

Lo anterior minora el texto contenido en el nodo line y luego verifica que (el texto en minúscula) contiene la palabra clave chicago.


Y ahora para el fragmento de código obligatorio (pero en realidad, lo anterior idea es lo que realmente necesita para llevar a casa):

$xml = simplexml_load_file($data); 
$search = strtolower($keyword); 
$nodes = $xml->xpath("//line[contains(translate(text(), 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'), '$search')]"); 

echo 'Got ' . count($nodes) . ' matches!' . PHP_EOL; 
foreach ($nodes as $node){ 
    echo $node . PHP_EOL; 
} 

Editar después dijon's comment

Dentro del foreach, puede acceder al número de línea, número de capítulo y nombre del libro, como ser bajo.

Número de línea - esto es solo un atributo en el elemento <line> que hace que acceder a él sea muy fácil. Hay dos formas, con SimpleXML, de acceder a ella: $node['number'] o $node->attributes()->number (prefiero la primera).

Número de capítulo - para llegar a esto, como bien dice, tenemos que atravesar el árbol. Si utilizáramos las clases DOM, tendríamos una útil propiedad $node->parentNode que nos llevaría directamente al <chapter> (ya que es el antecesor inmediato de nuestro <line>). SimpleXML no tiene una propiedad tan práctica, pero podemos usar una consulta relativa de XPath para obtenerla. El parent axis nos permite atravesar el árbol.

Dado que xpath() devuelve una matriz, podemos hacer trampa y usar current() para acceder al primer elemento (y único) de la matriz devuelta. Entonces solo se trata de acceder al atributo number como se indica arriba.

// In the near future we can use: current(...)['number'] but not yet 
$chapter = current($node->xpath('./parent::chapter'))->attributes()->number; 

nombre de libro - el proceso para esto es el mismo que el de acceso al número de capítulo. Una consulta XPath relativa del <line> podría hacer uso del ancestor axis como ./ancestor::book (o ./parent:chapter/parent::book). Con suerte, puede averiguar cómo acceder a su atributo name.

+0

Gracias por la explicación detallada de cómo funciona, además del fragmento de código. ¡Exactamente lo que estaba buscando! He estado utilizando principalmente XML simple para este proyecto, pero es bueno tener la respuesta de Gordon a continuación para comparar. – dijon

+0

Una cosa que me ENCANTARÍA saber :) está dentro de esa cláusula foreach, ¿Cómo también voy a enumerar el número de línea, el número de capítulo y el nombre del libro? Creo que esto también es xpath basado en el nodo actual y navegando por el árbol? por ejemplo, (a partir del primer ejemplo XML) Me gustaría buscar 'atlanta' y recibir: Años escolares, Capítulo 1: Aquí hay una línea que habla de Atlanta. una vez más, la prueba y error me ha estado atando en nudos! – dijon

+0

@dijon ver mi edición – salathe

2

Consulte la respuesta de salathe sobre cómo hacerlo con SimpleXml y translate().

Como una opción alternativa/agregada para usar las funciones de XPath, puede usar cualquier función de PHP a partir de PHP5.3, incluso autodefinida, en Expresiones XPath al usar DOM. No estoy seguro de que el mismo esté disponible en SimpleXml.

// create a DOMDocument and load your XML string into it 
$dom = new DOMDocument; 
$dom->loadXML($xml); 

// create a new Xpath and register PHP functions as XPath functions 
$xPath = new DOMXPath($dom); 
$xPath->registerNamespace("php", "http://php.net/xpath"); 
$xPath->registerPHPFunctions(); 

// Setup the query 
$keyword = 'chicago'; 
$q = "//line[php:functionString('stripos', text(), '$keyword')]"; 
$nodes = $xPath->query($q); 

// Iterate the resulting NodeList 
foreach($nodes as $node) { 
    echo $node->nodeValue, PHP_EOL; 
} 

Esta es la salida

Here's the first line with Chicago in it. 
Here's a line that says chicagogo 

Para más detalles, véase @salathes blog entry y the PHP Manual.

+0

+1 para insinuar el poder usar funciones PHP-land dentro de las consultas XPath (¡y el enlace a mi blog!). :) – salathe

+0

@sala por curiosidad: ¿sabes si hay alguna función que me permita usar DOMNodeList como si utilizara una matriz en array_map o un iterador en iterator_apply? A falta de usar '$ xpath-> query ('// book [php: function (" callback ", author)]');'? – Gordon

+0

"No estoy seguro de que el mismo esté disponible en SimpleXml". - No directamente, pero no hay nada que impida a las personas mezclar y combinar clases DOM/SimpleXML. :) – salathe

0

Puede que me haya perdido algo ... pero aquí hay otro enfoque que es IMHO - más simple. ¿Qué hay de usar PHP strtolower() antes de cargar el XML en SimpleXML a través de simplexml_load_string()?

IE

$xml = simplexml_load_string(strtolower(file_get_contents($xml_file_path))); 
$keyword = strtolower($_GET['keyword']); //Make sure you sanitize this! 
$kw = $xml->xpath("//line[contains(text(),'$keyword')]"); 

De esta manera, usted está comparando minúsculas :: minúscula