2011-04-17 3 views
4

Вы можете позвонить Nokogiri::XML::Node#ancestors.size, чтобы узнать, насколько глубоко вложен узел. Но есть ли способ определить, насколько глубоко вложен самый глубоко вложенный ребенок узла?Как вы рассчитываете количество «уровней» потомков узла Нокогири?

В качестве альтернативы, как вы можете найти все листовые узлы, которые спускаются с узла?

+0

Хороший вопрос, +1. См. Мой ответ для описания решения на основе XPath 1.0 и решения с одним XPath-2.0-выражением. –

+0

Связанный: [Как выбрать все листовые узлы с использованием выражения XPath?] (Http://stackoverflow.com/questions/3926589/how-to-select-all-leaf-nodes-using-xpath-expression) – Phrogz

ответ

1

Вы можете вызвать Nokogiri :: XML :: Node # ancestors.size для увидеть, как глубоко вложенный узел. Но есть способ определить, насколько глубоко вложен наиболее глубоко вложенный ребенок узел есть?

Применение:

count(ancestor::node()) 

Это выражение выражает количество ancesstors контекста (текущий) узел имеет в иерархии документа.

Чтобы найти уровень вложенности «наиболее глубоко вложенного ребенка», нужно сначала определить все «лист» узлов:

descendant-or-self::node()[not(node())] 

и для каждого из них получают их уровень вложенности, используя приведенное выше выражение XPath.

Затем должен быть рассчитан максимальный уровень вложенности (максимум всех произведенных чисел) и этот последний расчет невозможен с чистым XPath 1.0.

Это можно выразить в одном выражении XPath 2.0:

max(for $leaf in /descendant-or-self::node()[not(node())], 
     $depth in count($leaf/ancestor::node()) 
     return 
     $depth 
    ) 
+0

В настоящее время Nokogiri способный вычислять это выражение XPath 2.0? – dan

+0

@dan Я не знаю, что такое Нокогири. Если это всего лишь движок XPath 1.0, то он сам по себе не может вычислить необходимый максимум. В случае, если Nokogiri является API, который будет использоваться на определенном языке программирования, это можно сделать с помощью программы, написанной на этом конкретном языке программирования, и использования Nokogiri для оценки уровня вложенности каждого листового узла. –

+0

@dan Не пугайтесь высокой репутации Димитрия и впечатляющих знаний о XPath; если он не ответил на ваш вопрос, не принимайте его! :) _ С ** много ** должным уважением к Димитрию за его знания и помощь._ – Phrogz

1

Следующий код обезьяны-патчи Nokogiri::XML::Node для удовольствия, но, конечно, вы можете извлечь их как отдельные методы, берущих аргумент узла, если тебе нравится. (Только метод height является частью вашего вопроса, но я думал, что метод deepest_leaves может быть интересно.)

require 'nokogiri' 
class Nokogiri::XML::Node 
    def depth 
    ancestors.size 
    # The following is ~10x slower: xpath('count(ancestor::node())').to_i 
    end 
    def leaves 
    xpath('.//*[not(*)]').to_a 
    end 
    def height 
    tallest = leaves.map{ |leaf| leaf.depth }.max 
    tallest ? tallest - depth : 0 
    end 
    def deepest_leaves 
    by_height = leaves.group_by{ |leaf| leaf.depth } 
    by_height[ by_height.keys.max ] 
    end 
end 

doc = Nokogiri::XML "<root> 
    <a1> 
    <b1></b1> 
    <b2><c1><d1 /><d2><e1 /><e2 /></d2></c1><c2><d3><e3/></d3></c2></b2> 
    </a1> 
    <a2><b><c><d><e><f /></e></d></c></b></a2> 
</root>" 

a1 = doc.at_xpath('//a1') 
p a1.height      #=> 4 
p a1.deepest_leaves.map(&:name) #=> ["e1", "e2", "e3"] 
p a1.leaves.map(&:name)   #=> ["b1", "d1", "e1", "e2", "e3"] 

Редактировать: Для того, чтобы ответить только задал вопрос односложно, без упаковки его в ре неиспользованные детали:

p a1.xpath('.//*[not(*)]').map{ |n| n.ancestors.size }.max - a1.ancestors.size 
+0

Это более чем в три раза длиннее и много раз больше сложнее, чем одно выражение XPath 2.0! :) Очень хороший пример преимуществ использования чистых решений XPath над PL, даже если это будет Ruby. –

+0

@Dimitre Очень верно. (Ну ... в этом коде есть больше, чем нужно для одной и той же логики, но это не относится к делу.) Будет очень приятно, когда libxml2/Nokogiri поддерживает выражения XPath 2.0. Ваш отличный, краткий и правильный ответ просто не срабатывает для Нокигири, как об этом спрашивает ОП. – Phrogz

+0

@Dimitre На самом деле, неправда; за мое редактирование, практически без игры в гольф, код Ruby примерно на 1/3 меньше байта, чем XPath 2.0. * shrug * – Phrogz

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