Nokogiri比较字段和投影



我正在使用nokogiri解析XML文档,并希望输出产品名称与字符串匹配的位置列表。

我能够输出所有产品名称或所有位置列表的列表,但我无法比较这两个位置。删除语句的if部分正确输出所有位置。我的正则是我做错了什么?

@doc = Nokogiri::HTML::DocumentFragment.parse <<-EOXML
<?xml version="1.0"?>
<root>
<product>
  <name>cool_fish</name>
  <product_details>
    <location>ocean</location>
    <costs>
      <msrp>9.99</msrp>
      <margin>5.00</margin>
    </costs>
  </product_details>
</product>
<product>
  <name>veggies</name>
  <product_details>
    <location>field</location>
    <costs>
      <msrp>2.99</msrp>
      <margin>1.00</margin>
    </costs>
  </product_details>
</product>    
</root>
EOXML
doc.xpath("//product").each do |x|
  puts x.xpath("location") if x.xpath("name") =~ /cool_fish/
end

这里发生了一些事情:

  1. 正如其他人指出的那样,您应该解析为XML不是HTML,尽管这实际上对您获得的结果并没有太大影响。

  2. 您正在作为DocumentFragment解析,您应该作为完整的文档解析。有一些问题涉及查询文档片段,特别是从//开始的查询不正确。

  3. location元素实际上相对于XML中的product节点位于product_details/location位置,因此您需要更新查询以考虑到。

  4. 您正在尝试在xpath方法的结果(即Nokogiri::XML::NodeSet)的结果上使用=~运算符。NodeSet未定义=~方法,因此它使用Object上的默认方法,该方法仅返回nil,因此它永远不会匹配。您应该使用at_xpath仅获取第一个结果,然后在其上调用text,以获取可以使用=~匹配的字符串。

(您也使用@docdoc,但我假设这只是错字。)

因此,将这四个点结合在一起,您的代码看起来像:

#parse using XML, and not a fragment
doc = Nokogiri::XML <<-EOXML
  # ... XML elided for space
EOXML
doc.xpath("//product").each do |x|
  # correct query, use at_xpath and call text method
  puts x.at_xpath("product_details/location") if x.at_xpath("name").text =~ /cool_fish/
end

但是,在这种情况下,您可以使用contains函数在单个XPATH查询中完成所有操作:

# parse doc as XML document as above
puts doc.xpath("//product[contains(name, 'cool_fish')]/product_details/location")

这起作用是因为您有一个相当简单的正则是对文字字符串的检查。XPATH 1.0不支持正则表达式,因此,如果您的真实用例涉及更复杂的案例,则可能需要"困难的方式"。(在这种情况下,您可以编写自定义XPATH函数,但这是另一个故事。)

写下您的代码如下:

require 'nokogiri'
@doc = Nokogiri::XML <<-EOXML
<?xml version="1.0"?>
<root>
<product>
  <name>cool_fish</name>
  <product_details>
    <location>ocean</location>
    <costs>
      <msrp>9.99</msrp>
      <margin>5.00</margin>
    </costs>
  </product_details>
</product>
<product>
  <name>veggies</name>
  <product_details>
    <location>field</location>
    <costs>
      <msrp>2.99</msrp>
      <margin>1.00</margin>
    </costs>
  </product_details>
</product>    
</root>
EOXML

@doc.xpath("//product").each do |x|
    puts x.at_xpath(".//location").text  if x.at_xpath(".//name").text =~ /cool_fish/
end
# >> ocean

您正在解析xml,应该使用Nokogiri::XML。您的xpath表达式也不正确。您写了#xpath方法,但是您正在使用表达式,您应该将其与csssearch之类的方法一起使用。我使用at_xpath方法,因为您对#each block中的单节点匹配感兴趣。

但是您可以使用at代替#at_xpathsearch代替xpath

记住searchat都可以理解 css 以及 xpath 表达式。searchxpathcss所有方法都将为您提供NodeSet,其中atat_cssat_xpath将为您提供Node。一旦Nokogiri节点掌握在您的手中,请使用text方法获取该节点的内容。

我建议使用nokogiri :: xml

@doc = Nokogiri::XML::Document.parse <<-EOXML
<?xml version="1.0"?>
<root>
<product>
  <name>cool_fish</name>
  <product_details>
    <location>ocean</location>
    <costs>
      <msrp>9.99</msrp>
      <margin>5.00</margin>
    </costs>
  </product_details>
</product>
<product>
  <name>veggies</name>
  <product_details>
    <location>field</location>
    <costs>
      <msrp>2.99</msrp>
      <margin>1.00</margin>
    </costs>
  </product_details>
</product>    
</root>
EOXML

,然后Nokogiri :: Node#search and Nokogiri :: Node#at Methods

@doc.search("product").each do |x|
  puts x.at("location").content if x.at("name").content =~  /cool_fish/
end

相关内容

  • 没有找到相关文章

最新更新