2014-02-14 4 views
2

С nokogiri легко получить абсолютный путь от любого узла до корня, просто позвонив node.path. Возьмем такой пример:Простейший способ получить xpath между двумя узлами

<bookstore> 
    <department category="COOKING"> 
    <book> 
     <title lang="en">Everyday Italian</title> 
     <author>Giada De Laurentiis</author> 
     <year>2005</year> 
     <price>30.00</price> 
    </book> 
    <book> 
     <title lang="en">Nice meals</title> 
     <author>J K. Rowling</author> 
     <year>2005</year> 
     <price>29.99</price> 
    </book> 
    </department> 
    <department category="WEB"> 
    <book> 
     <title lang="en">Learning XML</title> 
     <author>Erik T. Ray</author> 
     <year>2003</year> 
     <price>39.95</price> 
    </book> 
    </department> 
</bookstore> 

Если бы я сделал

tree.search("//title[text() = 'Learning XML']").first.path 

Я хотел бы получить что-то вроде bookstore/department[2]/book[1]/title[1]

Теперь, что если бы я хотел, чтобы получить путь к этому узлу, но вместо того, чтобы с корнем , Я хотел получить его, например //department[@category='WEB'] и вплоть до одного и того же узла заголовка?

Иными словами. Как я могу получить/генерировать путь между двумя известными узлами, такими как //department[@category='WEB'], до bookstore/department[2]/book[1]/title[1]?

EDIT

Я думал о том, какой способ превращения //department[@category='WEB'] в новый вид «корня», например, путем удаления чего-либо, а затем снова использовать метод .path на узле заголовка. Это, кажется, не очень «простой, хотя» ...

+0

вы пробовали 'дерево .xpath ("// department [@ category = 'WEB']/book [1]/title [1]") '? – Abdo

+0

Хе-хе, это результат, но я хочу, чтобы он смог сгенерировать этот результат программно из знания двух узлов // department [@ category = 'WEB'] и книжного магазина/отдела [2]/book [1]/title [1] –

+0

Поскольку у вас всегда есть одна и та же структура, '' // department [@ category = 'WEB']/"+" книжный магазин/отдел [2]/book [1]/title [1] ". split ("/"). drop (2) .join ("/") ' – Abdo

ответ

1

Я не в восторге от строки на основе хака в конце, но теперь это производит чистые XPaths:

require 'nokogiri' 
class Nokogiri::XML::Node 
    def path_to(node) 
    self_ancestors = [self].concat(self.ancestors) 
    shared = (self_ancestors & [node].concat(node.ancestors)).first 
    [ "../"*self_ancestors.index(shared), 
     ".", node.path[shared.path.length..-1] ] 
     .join 
     .sub(%r{\A\./|/\.(?=/|\z)}, '') # remove superfluous "." 
    end 
end 

doc = Nokogiri.XML(IO.read('tmp.rxml')) 
n1 = doc.at("//department[@category='WEB']") 
n2 = doc.at("//title[.='Learning XML']") 
n3 = doc.at("//year[.='2003']") 
n4 = doc.at("//@lang") 

p n1.path_to(n2) #=> "book/title" 
p n2.path_to(n3) #=> "../year" 
p n3.path_to(n2) #=> "../title" 
p n2.path_to(n1) #=> "../.." 
p n1.path_to(n1) #=> "." 
p n4.path_to(n2) #=> "../../../../department[2]/book/title" 
p n2.path_to(n4) #=> "../../../department[1]/book[1]/title/@lang" 

p n2.at(n2.path_to(n4))==n4 #=> true 
+0

Хм довольно умный, однако я действительно не буду использовать имена элементов вместо «любого» элемента, как это делает метод «путь». –

+0

@NielsKristian Ваши спецификации неясны. Пожалуйста, укажите точный пример нужного результата, в сочетании с по меньшей мере одним или двумя примерами входов/узлов, которые должны создавать выход. XPath выше * * использует команду 'path', кроме тех случаев, когда использует' ..' (что однозначно) или '.' (что также однозначно). Единственное, что «нечисто» относится к XPath, - это лишнее присутствие «.», Базового no-op. – Phrogz

+0

Там я редактировал метод для создания чистого вывода. Как было отмечено в ответе, я хотел бы найти более чистый способ удалить «.» (Например, не создавая их, если это необходимо), но это просто пуристические желания. – Phrogz

Смежные вопросы