2011-01-14 22 views
32

Estoy tratando de extraer el elemento dc:title usando un xpath. Puedo extraer los metadatos usando el siguiente código.Consulta del espacio de nombres Nokogiri/Xpath

doc = <<END 
<?xml version="1.0" encoding="UTF-8"?> 
<package xmlns="http://www.idpf.org/2007/opf" version="2.0"> 
    <metadata xmlns:dc="URI"> 
    <dc:title>title text</dc:title> 
    </metadata> 
</package> 
END 

doc = Nokogiri::XML(doc) 

# Awesome this works! 
puts '//xmlns:metadata' 
puts doc.xpath('//xmlns:metadata') 
# => <metadata xmlns:dc="URI"><dc:title>title text</dc:title></metadata> 

Como puede ver, todo lo anterior parece funcionar correctamente. Sin embargo, parece que no puedo obtener la información del título de este árbol de nodos, todos los siguientes fallan.

puts doc.xpath('//xmlns:metadata/title') 
# => nil 

puts doc.xpath('//xmlns:metadata/dc:title') 
# => ERROR: `evaluate': Undefined namespace prefix 

puts doc.xpath('//xmlns:dc:title') 
# => ERROR: 'evaluate': Invalid expression: //xmlns:dc:title 

¿Podría alguien explicar cómo se deben usar los espacios de nombres en un xpath con el documento xml anterior?

Respuesta

60

Todos los espacios de nombres deben registrarse durante el análisis. Nokogiri registra automáticamente los espacios de nombres en el nodo raíz. Cualquier espacio de nombres que no esté en el nodo raíz debe registrarse usted mismo. Esto debería funcionar:

puts doc.xpath('//dc:title', 'dc' => "URI") 

Como alternativa, puede eliminar espacios de nombres por completo. Solo haga esto si está seguro de que no habrá nombres de nodo conflictivos.

doc.remove_namespaces! 
puts doc.xpath('//title') 
+0

Awesome funcionó perfectamente, gracias! – Jamie

+3

+1 Sí remove_namespaces FTW! –

+1

¡Gracias! ¡esto es magia! – Jirapong

1

Con prefijo opf debidamente registrada para 'http://www.idpf.org/2007/opf' URI de espacio, y dc para 'URI', se necesita:

/*/opf:metadata/dc:title 

Nota: xmlns y xml están reservados prefijos que no se pueden unir a cualquier otro espacio de nombres URI que el incorporado 'http://www.w3.org/2000/xmlns/' y 'http://www.w3.org/XML/1998/namespace'.

+0

Parece que no funcionó doc.xpath ('/ */opf: metadata/dc: title') # => "' evaluate ': Prefijo de espacio de nombres indefinido " – Jamie

+0

@Jamie: ¿Has leído la respuesta? Comienza la primera oración * "Con el prefijo debidamente registrado" * ... –

+0

@Alejandro, disculpas, no entiendo del todo que haya una manera de hacerlo sin el prefijo para opf (excepto la forma descrita en @ mark-thomas respuesta), Sería bueno hacerlo en una consulta xpath. – Jamie

0

Como alternativa a la construcción de forma explícita un hash de los URI de espacio de nombres, puede recuperar las definiciones de espacio de nombres del elemento XML donde están definidos.

Usando su ejemplo:

# First grab the metadata node, because that's where "dc" is defined. 
metadata = doc.at_xpath('//xmlns:metadata') 

# Pass metadata's namespaces as the resolver. 
metadata.at_xpath('dc:title', metadata.namespaces) 

Tenga en cuenta que el segundo XPath también podría haber sido:

doc.at_xpath('//dc:title', metadata.namespaces).to_s 

Pero ¿por qué la búsqueda de la raíz cuando se tiene un ancestro más cerca? Además, debe considerar el elemento que define el espacio de nombres más sus elementos secundarios como el "alcance" del espacio de nombres. Buscar un alcance limitado es menos confuso y evita errores sutiles.

Cuestiones relacionadas