Как использовать xpath на узлах с префиксом, но без пространства имен?
У меня есть файл XML, который мне нужно проанализировать. Я не контролирую формат файла и не могу его изменить.
Файл использует префикс (назовите его a
), но он нигде не определяет пространство имен для этого префикса. Я не могу использовать xpath
запросить узлы с a
Пространство имен.
Вот содержимое документа XML
<?xml version="1.0" encoding="UTF-8"?>
<a:root>
<a:thing>stuff0</a:thing>
<a:thing>stuff1</a:thing>
<a:thing>stuff2</a:thing>
<a:thing>stuff3</a:thing>
<a:thing>stuff4</a:thing>
<a:thing>stuff5</a:thing>
<a:thing>stuff6</a:thing>
<a:thing>stuff7</a:thing>
<a:thing>stuff8</a:thing>
<a:thing>stuff9</a:thing>
</a:root>
Я использую Nokogiri для запроса документа:
doc = Nokogiri::XML(open('text.xml'))
things = doc.xpath('//a:thing')
Сбой дает следующую ошибку:
Nokogiri::XML::XPath::SyntaxError: Undefined namespace prefix: //a:thing
Из моего исследования я обнаружил, что могу указать пространство имен для префикса в xpath
метод:
things = doc.xpath('//a:thing', a: 'nobody knows')
Это возвращает пустой массив.
Что было бы для меня лучшим способом получить нужные мне узлы?
1 ответ
Проблема заключается в том, что пространство имен не определено должным образом в документе XML. В результате Nokogiri видит имена узлов как "a:root" вместо "a" как пространство имен, а "root" как имя узла:
xml = %Q{
<?xml version="1.0" encoding="UTF-8"?>
<a:root>
<a:thing>stuff0</a:thing>
<a:thing>stuff1</a:thing>
</a:root>
}
doc = Nokogiri::XML(xml)
puts doc.at_xpath('*').node_name
#=> "a:root"
puts doc.at_xpath('*').namespace
#=> ""
Решение 1. Укажите имя узла с двоеточием
Одним из решений является поиск узлов с именем "a: thing". Вы не можете сделать //a:thing
поскольку XPath будет обрабатывать "а" как пространство имен. Вы можете обойти это, делая //*[name()="a:thing"]
:
xml = %Q{
<?xml version="1.0" encoding="UTF-8"?>
<a:root>
<a:thing>stuff0</a:thing>
<a:thing>stuff1</a:thing>
</a:root>
}
doc = Nokogiri::XML(xml)
things = doc.xpath('//*[name()="a:thing"]')
puts things
#=> <a:thing>stuff0</a:thing>
#=> <a:thing>stuff1</a:thing>
Решение 2. Изменить XML-документ, чтобы определить пространство имен
Альтернативное решение - изменить файл XML, который вы получаете, чтобы правильно определить пространство имен. Документ будет вести себя с пространствами имен, как и ожидалось:
xml = %Q{
<?xml version="1.0" encoding="UTF-8"?>
<a:root>
<a:thing>stuff0</a:thing>
<a:thing>stuff1</a:thing>
</a:root>
}
xml.gsub!('<a:root>', '<a:root xmlns:a="foo">')
doc = Nokogiri::XML(xml)
things = doc.xpath('//a:thing')
puts things
#=> <a:thing>stuff0</a:thing>
#=> <a:thing>stuff1</a:thing>