Ruby / Nokogiri / Xpath неглубокий поиск - PullRequest
1 голос
/ 25 февраля 2011

Код, примененный к (упрощенному) xml ниже, возвращает мне узел с myanim1.swf, как и ожидалось.Я на самом деле хочу узел с myanim3.swf.Я хотел бы сказать «получить первый узел с тегом свойства с атрибутом name = animationUrl, но не углубляться больше, чем один дочерний элемент».В настоящее время у меня есть обходной путь, но я ищу более элегантное решение.

Это рубин, а объект - объект Нокогири.Я полагаю, что это поиск XPath, но мне немного неясно, является ли это вкусом XPath Нокогири.В любом случае, помощь приветствуется, спасибо.

XML:

<blah>
    <property name="overlays">
        <array>
            <object type="myType">
                <property name="animationUrl" value="myanim1.swf"/>
            </object>
            <object type="myType">
                <property name="animationUrl" value="myanim2.swf"/>
            </object>
        </array>
    </property>
    <property name="animationUrl" value="myanim3"/>
    </property>
</blah>

КОД:

myNode = thisNode.search("property[name=animationUrl]").first.andand["value"]

Ответы [ 4 ]

2 голосов
/ 26 февраля 2011

получить первый узел с тегом свойства с атрибутом name = animationUrl, но не углубляйся глубже, чем один ребенок

Относительное выражение XPath:

*[property/@name='animationUrl'][1]

Это означает: первый дочерний элемент узла контекста, имеющий хотя бы один property дочерний элемент с атрибутом @name, равным 'animationUrl'

Абсолютные выражения XPath:

/*[property/@name='animationUrl']

Это означает: корневой элемент, имеющий хотя бы один property дочерний элемент с атрибутом @name, равным 'animationUrl'

/descendant::*[property/@name='animationUrl'][1]

Это означает: первый дочерний элемент корня документа, имеющий хотя бы один property дочерний элемент с атрибутом @name, равным 'animationUrl'

1 голос
/ 25 февраля 2011

Вам необходимо использовать at_xpath вместе с абсолютной ссылкой на узел, которая будет указывать, что вы хотите извлекать узлы только на глубине <blah><property>:

my_node = xml.at_xpath("/blah/property[@name='animationUrl']")

И в действии:

require 'nokogiri'
thisNode = <<XML 
<blah>
    <property name="overlays">
        <array>
            <object type="myType">
                <property name="animationUrl" value="myanim1.swf"/>
            </object>
            <object type="myType">
                <property name="animationUrl" value="myanim2.swf"/>
            </object>
        </array>
    </property>
    <property name="animationUrl" value="myanim3"/>
    </property>
</blah>
XML

xml=Nokogiri::XML(thisNode)
my_node = xml.at_xpath("/blah/property[@name='animationUrl']")
puts my_node   #=> <property name="animationUrl" value="myanim3"/>
1 голос
/ 25 февраля 2011

Если вы знаете глубину нужного вам предмета, вы можете сделать это:

p d.at_xpath('//property[@name="animationUrl"][count(ancestor::*) = 1]')

Однако, если вы знаете глубину, тогда, конечно, это легче сделать:

p d.at_xpath('/*/property[@name="animationUrl"]')

Если вы хотите выполнить поиск в ширину, чтобы найти самый мелкий предмет на любой глубине:

Хотя XPath позволяет вам определять глубину элемента (как показано выше), он не позволяет вам сортировать элементы в возвращенном NodeSet (что вам и нужно сделать в чистом XPath). Поэтому нам придется прибегнуть к использованию Ruby для общего случая:

class Nokogiri::XML::Node
  def at_shallowest_xpath( path )
    xpath(path).sort_by{ |n| n.xpath 'count(ancestor::*)' }.first
  end
end

d = Nokogiri::XML xml_source
puts d.at_shallowest_xpath('//property[@name="animationUrl"]')
#=> <property name="animationUrl" value="myanim3"/>

Редактировать : Использование XPath для нахождения глубины, хотя и хитрое, примерно вдвое медленнее (на моей машине с данными этого теста) как запрос Нокогири найти глубину элемент для вас. Вместо этого используйте это:

class Nokogiri::XML::Node
  def at_shallowest_xpath( path )
    xpath(path).sort_by{ |n| n.ancestors.length }.first
  end
end
1 голос
/ 25 февраля 2011

Я думаю, вам нужно что-то вроде:

t.xpath("/blah/property[@name='animationUrl']")
...