如何解析XML并将所有相同的命名节点值放入数组中

问题描述 投票:0回答:2

我有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>

我已经尝试同时使用cssxpath来检索每个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)
ruby xml nokogiri
2个回答
2
投票

除了Greg的响应(XML需要一个包含元素)之外,您的XPath表达式选择了错误的内容:

 //*[@a:Status]

选择具有a:Status 属性的所有元素。如果要所有具有子a:Status元素的元素,只需从节点测试中删除@

 //*[a:Status]

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>]

但是将其添加后,文档的错误列表要小得多:

(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”。

© www.soinside.com 2019 - 2024. All rights reserved.