Запрос пространства имен Nokogiri/Xpath
Я пытаюсь вытащить элемент dc:title
, используя 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>
Как видно, вышеприведенное работает правильно. Однако, похоже, я не могу получить информацию о названии из этого дерева node, все из нижеперечисленных сбоев.
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
Может кто-нибудь объяснить, как пространства имен должны использоваться в xpath с указанным выше документом xml.
Ответы
Ответ 1
Все пространства имен должны регистрироваться при разборе. Nokogiri автоматически регистрирует пространства имен в корневом каталоге node. Любые пространства имен, которые не находятся в корневом каталоге node, вам необходимо зарегистрировать себя. Это должно работать:
puts doc.xpath('//dc:title', 'dc' => "URI")
В качестве альтернативы вы можете полностью удалить пространства имен. Только сделайте это, если вы уверены, что не будет конфликтующих имен node.
doc.remove_namespaces!
puts doc.xpath('//title')
Ответ 2
С правильно зарегистрированным префиксом opf
для 'http://www.idpf.org/2007/opf'
URI пространства имен и dc
для 'URI'
вам нужно:
/*/opf:metadata/dc:title
Примечание: xmlns
и xml
являются зарезервированными префиксами, которые не могут быть привязаны к любому другому URI пространства имен, чем встроенные 'http://www.w3.org/2000/xmlns/'
и 'http://www.w3.org/XML/1998/namespace'
.
Ответ 3
В качестве альтернативы явным образом создавая хэш URI пространства имен, вы можете получить определения пространства имен из элемента xml, где они определены.
Используя ваш пример:
# First grab the metadata node, because that where "dc" is defined.
metadata = doc.at_xpath('//xmlns:metadata')
# Pass metadata namespaces as the resolver.
metadata.at_xpath('dc:title', metadata.namespaces)
Обратите внимание, что второй xpath также мог быть:
doc.at_xpath('//dc:title', metadata.namespaces).to_s
Но зачем искать из корня, когда у вас есть ближайший предок? Кроме того, вы должны учитывать элемент, определяющий пространство имен, и его дочерние элементы как "область" пространства имен. Поиск ограниченного объема менее запутанным и позволяет избежать тонких ошибок.