Одна из идей XPath заключается в том, что она позволяет нам перемещаться по DOM аналогично каталогу на диске:
require 'hpricot'
xml = <<EOT
<rss>
<channel>
<item>
<title>Book1</title>
<pubDate>march 1 2010</pubDate>
<author>Bob</author>
</item>
<item>
<title>book2</title>
<pubDate>october 4 2009</pubDate>
<author>Bill</author>
</item>
<item>
<title>book3</title>
<pubDate>June 5 2010</pubDate>
<author>Steve</author>
</item>
<item>
<title>Book4</title>
<pubDate>march 1 2010</pubDate>
<author>Bob</author>
</item>
</channel>
</rss>
EOT
doc = Hpricot(xml)
titles = (doc / '//author[text()="Bob"]/../title' )
titles # => #<Hpricot::Elements[{elem <title> "Book1" </title>}, {elem <title> "Book4" </title>}]>
Это означает: «найдите все книги Боба, затем найдите один уровень и найдитетег заголовка ".
Я добавил дополнительную книгу" Боб ", чтобы проверить получение всех вхождений.
Чтобы получить элемент, содержащий книгу Боба, просто вернитесь на уровень вверх:
items = (doc / '//author[text()="Bob"]/..' )
puts items # => nil
# >> <item>
# >> <title>Book1</title>
# >> <pubdate>march 1 2010</pubdate>
# >> <author>Bob</author>
# >> </item>
# >> <item>
# >> <title>Book4</title>
# >> <pubdate>march 1 2010</pubdate>
# >> <author>Bob</author>
# >> </item>
Я также понял, что делает (doc % :rss % :channel / :item)
.Это эквивалентно вложению поисков, за исключением заключенных в скобки скобок, и они должны быть одинаковыми в Hpricot-ese:
(doc % :rss % :channel / :item).size # => 4
(((doc % :rss) % :channel) / :item).size # => 4
(doc / '//rss/channel/item').size # => 4
(doc / 'rss channel item').size # => 4
Поскольку '//rss/channel/item'
- это то, как вы обычно видите средство доступа XPath, и 'rss channel item'
- это аксессор CSS, я бы рекомендовал использовать эти форматы для удобства и ясности.