简体   繁体   中英

Ruby / Nokogiri / Xpath shallow search

The code applied to the (simplified) xml below gets me the node with myanim1.swf, as expected. I actually want the node with myanim3.swf. I would like to say "get the first node with a property tag with attribute name=animationUrl, but don't go deeper than one child". I currently have a work-around, but I'm looking for a more elegant solution.

This is ruby, and the object is a Nokogiri object. I believe this is XPath searching, but I'm a little unclear as to whether it is Nokogiri's flavor of XPath. Anyway, help appreciated, thanks.

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>

CODE:

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

get the first node with a property tag with attribute name=animationUrl, but don't go deeper than one child

Relative XPath expression:

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

This means: context node's first child element having at least one property child element with @name attribute equal to 'animationUrl'

Absolute XPath expressions:

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

This means: root element having at least one property child element with @name attribute equal to 'animationUrl'

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

This means: document root's first descendant element having at least one property child element with @name attribute equal to 'animationUrl'

我认为您需要以下内容:

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

You need to use at_xpath along with an absolute reference to the node, which will specify you only want to retrieve nodes at the <blah><property> depth:

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

And in action:

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"/>

If you know the depth of the item you want, you could do this:

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

However, if you know the depth, then of course it's easier to do:

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

If you wanted an actual breadth-first search that found the most shallow item at any depth:

While XPath lets you determine the depth of an item (as shown above), it does not let you sort items in the returned NodeSet (which is what you'd need to do this in pure XPath). As such, we'll have to resort to using Ruby for the general case:

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"/>

Edit : Using XPath to find the depth, while tricky, is about twice as slow (on my machine, with this test data) as asking Nokogiri to find the depth of the element for you. Instead, use this:

class Nokogiri::XML::Node
  def at_shallowest_xpath( path )
    xpath(path).sort_by{ |n| n.ancestors.length }.first
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM