web-dev-qa-db-fra.com

Requête d'espace de noms Nokogiri/Xpath

J'essaie d'extraire l'élément dc:title en utilisant un xpath. Je peux extraire les métadonnées en utilisant le code suivant.

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>

Comme vous pouvez le constater, ce qui précède semble fonctionner correctement. Cependant, il semble que je ne puisse pas obtenir les informations de titre de cette arborescence de noeuds, tous les éléments ci-dessous échouent.

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

Quelqu'un pourrait-il s'il vous plaît expliquer comment les espaces de noms doivent être utilisés dans un xpath avec le doc xml ci-dessus.

35
Jamie

Tous les espaces de noms doivent être enregistrés lors de l'analyse. Nokogiri enregistre automatiquement les espaces de noms sur le nœud racine. Tous les espaces de noms qui ne sont pas sur le nœud racine doivent être enregistrés par vous-même. Cela devrait fonctionner:

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

Alternativement, vous pouvez supprimer complètement les espaces de noms. Ne le faites que si vous êtes certain qu'il n'y aura pas de noms de nœuds en conflit.

doc.remove_namespaces!
puts doc.xpath('//title')
65
Mark Thomas

Avec le préfixe correctement enregistré opf pour 'http://www.idpf.org/2007/opf' URI d'espace de nom et dc pour 'URI', vous avez besoin des éléments suivants:

/*/opf:metadata/dc:title

Remarque : xmlns et xml sont des préfixes réservés qui ne peuvent être liés à aucun autre URI d'espace de nom que les 'http://www.w3.org/2000/xmlns/' et 'http://www.w3.org/XML/1998/namespace' intégrés.

1
user357812

Au lieu de créer explicitement un hachage d'URI d'espace de nom, vous pouvez récupérer les définitions d'espace de nom à partir de l'élément xml où elles ont été définies.

En utilisant votre exemple:

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

Notez que le second xpath aurait aussi pu être:

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

Mais pourquoi chercher à la racine quand vous avez un ancêtre plus proche? En outre, vous devez considérer l'élément définissant l'espace de nom, ainsi que ses enfants, comme la "portée" de l'espace de nom. Rechercher dans un champ limité est moins déroutant et évite les bugs subtils.

0
Kelvin