我有一个XML,我试图从内存中解析并使用Nokogiri获取每个心跳测试的状态。这是我的解决方案......
xml =
<a:HBeat>
<a:ElapsedTime>3 ms</a:ElapsedTime>
<a:Name>Service 1</a:Name>
<a:Status>true</a:Status>
</a:HBeat>
<a:HBeat>
<a:ElapsedTime>4 ms</a:ElapsedTime>
<a:Name>Service 2</a:Name>
<a:Status>true</a:Status>
</a:HBeat>
<a:HBeat>
我尝试使用css和xpath来拉回每个Status的值并将其放入数组中。代码如下:
doc = Nokogiri::XML.parse(xml)
#service_state = doc.css("a:HBeat, a:Status", 'a' => 'http://schemas.datacontract.org/2004/07/OpenAPI.Entity').map {|node| node.children.text}
service_state = doc.xpath("//*[@a:Status]", 'a' => 'http://schemas.datacontract.org/2004/07/OpenAPI.Entity').map(&:text)
两者都将返回service_state = []。有什么想法或建议吗?
另外,考虑到我有几乎相同的xml用于另一个测试,我使用了以下代码片段,它完全符合我的要求,但由于某种原因不能使用包含命名空间的xml。
service_state = doc.css("HBeat Status").map(&:text)
谢谢!
答案 0 :(得分:2)
部分问题是您的XML示例不正确:虽然您正在使用名称空间,但您缺少名称空间声明,并且您缺少包含标记。第一个可以解决,但第二个需要调整XML。
require 'nokogiri'
require 'pp'
xml = <<EOT
<xml xmlns:a="http://schemas.datacontract.org/2004/07/OpenAPI.Entity"> # <-- changed
<a:HBeat>
<a:ElapsedTime>3 ms</a:ElapsedTime>
<a:Name>Service 1</a:Name>
<a:Status>true</a:Status>
</a:HBeat>
<a:HBeat>
<a:ElapsedTime>4 ms</a:ElapsedTime>
<a:Name>Service 2</a:Name>
<a:Status>true</a:Status>
</a:HBeat>
<a:HBeat>
</xml>
EOT
doc = Nokogiri::XML(xml)
service_state = doc.css('a|Status').map(&:text) # <-- changed to show CSS with namespace
pp service_state
service_state = doc.search('//a:Status').map(&:text) # <-- added
pp service_state # <-- added
>> ruby test.rb
>> ["true", "true"]
>> ["true", "true"] # <-- added
命名空间是一件好事,但是当你想要做的只是获取数据时,处理它们会很痛苦。 Nokogiri有一些技巧可以让它们变得不那么烦人,例如像我上面那样使用CSS访问器,这意味着“在所有命名空间中找到Status标签”,所以即使没有声明命名空间,它仍然会很好。
如果您控制XML,那么您可以取消命名空间。它们在处理可能的标记冲突时非常好,但是当您拥有生成文件的机制时,这种情况不太可能,因此,如果是这种情况,您可以放弃它们。如果你需要命名空间,那么它应该被声明为:
<xml xmlns:a="http://schemas.datacontract.org/2004/07/OpenAPI.Entity">
没有它,XML会解析大量的命名空间错误:
(rdb:1) pp doc.errors
[#<Nokogiri::XML::SyntaxError: Namespace prefix a on HBeat is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on ElapsedTime is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on Name is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on Status is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on HBeat is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on ElapsedTime is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on Name is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on Status is not defined>,
#<Nokogiri::XML::SyntaxError: Namespace prefix a on HBeat is not defined>,
#<Nokogiri::XML::SyntaxError: Opening and ending tag mismatch: HBeat line 12 and xml>,
#<Nokogiri::XML::SyntaxError: Premature end of data in tag xml line 1>]
但是在添加之后,doc的错误列表要小得多:
(rdb:1) pp doc.errors
[#<Nokogiri::XML::SyntaxError: Opening and ending tag mismatch: HBeat line 12 and xml>,
#<Nokogiri::XML::SyntaxError: Premature end of data in tag xml line 1>]
另请参阅“How to avoid joining all text from Nodes when scraping”。
答案 1 :(得分:2)
除了Greg的响应(XML需要包含元素)之外,你的XPath表达式选择了错误的东西:
//*[@a:Status]
选择具有以下内容的所有元素:Status attributes 。如果您希望所有具有子项的元素为:Status元素,只需从节点测试中删除@
:
//*[a:Status]