我正在使用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
答案 0 :(得分:2)
这里发生了一些事情:
正如其他人所指出的那样,你应该解析为XML而不是HTML,尽管这对你得到的结果实际上没有太大的影响。
您正在解析为DocumentFragment
,您应该将其解析为完整的文档。查询文档片段存在一些问题,特别是以//
开头的查询无法正常工作。
location
元素实际上位于XML中product_details/location
节点的product
位置,因此您需要更新查询以将其考虑在内。< / p>
您正尝试在xpath
method =~
的结果上使用Nokogiri::XML::NodeSet
运算符。 NodeSet
未定义=~
方法,因此它使用default one on Object
that just returns nil
,因此它永远不会匹配。您应该使用at_xpath
仅获取第一个结果,然后在其上调用text
以获取您可以使用=~
匹配的字符串。
(你也使用@doc
和doc
,但我认为这只是一个错字。)
因此,将您的代码看起来像这四点:
#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
function在单个XPath查询中完成所有操作:
# parse doc as XML document as above
puts doc.xpath("//product[contains(name, 'cool_fish')]/product_details/location")
这是有效的,因为你有一个相当简单的正则表达式,只检查文字字符串。 XPath 1.0不支持正则表达式,所以如果你的真实用例涉及更复杂的用例,你可能需要“艰难的”方式。 (在这种情况下,您可以编写自定义XPath函数,但这是另一个故事。)
答案 1 :(得分:1)
编写如下代码:
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
方法,但是您使用的是表达式,您应该使用css
或search
等方法。我使用了at_xpath
方法,因为您对#each
块中的单节点匹配感兴趣。
但您可以使用at
代替#at_xpath
和search
代替xpath
。
请记住search
和at
都了解 CSS 以及 xpath 表达式。 search
或xpath
或css
所有方法都会为您NodeSet
提供at
,at_css
或at_xpath
给您的方式一个Node
。一旦Nokogiri
节点出现在您手中,请使用text
方法获取该节点的内容。
答案 2 :(得分:1)
我建议使用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和Nokogiri::Node#at方法
@doc.search("product").each do |x|
puts x.at("location").content if x.at("name").content =~ /cool_fish/
end