2011-02-25 93 views
1

应用于(简化)xml下面的代码使myanim1.swf,如预期的节点。我其实希望节点myanim3.swf。我想说的是“获得属性名为animationUrl的属性标记的第一个节点,但不要超过一个孩子”。我目前有一个解决方法,但我正在寻找一个更优雅的解决方案。红宝石/ Nokogiri/Xpath浅搜索

这是红宝石,对象是一个Nokogiri对象。我相信这是XPath搜索,但我对Nokogiri的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> 

CODE:

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

Nokogiri的XPath风味? Nokogiri使用libxml2,这是一个标准的XML解析器。 – 2011-03-01 03:21:58

回答

1

我想你需要的东西,如:

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

+1这就是我要用的。 – 2011-03-01 03:25:55

+0

我想你的意思是'at_xpath'? – Phrogz 2011-03-02 15:49:30

1

您需要使用具有绝对的参考节点一起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"/> 
+0

你不*有*使用'at_xpath'。你可以使用'at'。 Nokogiri会做正确的事情。 – 2011-03-01 03:24:50

+0

使用at将在指定级别返回具有animationUrl的对象的所有出现次数。 at_xpath只会返回操作查找的第一个事件。 – 2013-04-22 09:49:45

1

如果你知道你想要的项目的深度,你可以这样做:

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

但是,如果你知道的深度,然后当然这很容易做到:

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

如果你想要一个实际的广度优先搜索发现最浅的项目在任何深度:

虽然XPath可以让你决定一个项目的深度(如上图所示),它不会让你排序中的项目返回的NodeSet(这是你需要在纯XPath中完成的)。因此,我们将不得不诉诸使用红宝石一般情况下:

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查找的深度,而棘手的,大约是两倍慢(我的机器上,用这个测试数据)询问Nokogiri为你找到元素的深度。相反,使用这样的:

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

我想你误解了OP的意图。他只想提取一个根节点级别的节点。他不需要计算深度。 – 2011-02-26 01:05:04

+0

@MarkThomas阿克,你是对的。我完全错过了“但不要超过一个孩子”的话。我误解了“浅搜索”是指“广度优先,先找到最浅的一个”。哦,那么答案的最上面部分仍然有效,至少:) – Phrogz 2011-02-26 01:44:24

2

得到与属性名称= animationUrl属性标签 的第一个节点,但 不要去深度超过一个孩子

相对XPath表达式:

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

这意味着:上下文节点的第一个子元素至少有一个与@name 10子元素属性等于'animationUrl'

绝对XPath表达式:

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

这意味着:根元素具有至少一个property@name子元素等于属性'animationUrl'

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

这意味着:文档根的f具有至少一个property子元素的第一后裔元素@name属性等于'animationUrl'

+0

作为病人教育家+1。 :) – 2011-02-26 04:14:03