【问题标题】:How to parse XML and put all the same named node values into an array如何解析 XML 并将所有相同的命名节点值放入数组中
【发布时间】:2011-05-29 02:58:03
【问题描述】:

我有 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 来检索每个状态的值并将其放入一个数组中:

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,并且我使用了以下 sn-p 代码,它完全符合我的要求,但由于某种原因不能使用包含命名空间的 XML:

service_state = doc.css("HBeat Status").map(&:text)

【问题讨论】:

    标签: ruby xml nokogiri


    【解决方案1】:

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

     //*[@a:Status]
    

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

     //*[a:Status]
    

    【讨论】:

    • 我修改了我的 XPath 表达式以省略“@”但是当我这样做时,我得到了所有的东西......所以它看起来像:2 msService 1true 2 msService 2true 如果它会很棒以某种方式隔开或分隔。
    • doc.xpath() 返回匹配的元素列表。所以你可以对他们做任何你想做的事。但是,在您的示例中,您已将列表映射到 &:text,它只是连接所有后代元素的文本。例如。 doc.xpath("//*[a:Status]", # namespace).map { |node| # do something with node }
    【解决方案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”。

    【讨论】:

    • XML 仍然不正确。 (额外的&lt;a:HBeat&gt;,原版中也有)
    • 命名空间存在于实际的 XML 中。我在原始帖子中并不清楚它是否包含在我的 XML sn-p 中,但没有包含在我的 XML sn-p 中。此时我无法控制命名空间,因为该服务还有其他几个消费者。
    • Nokogiri 更喜欢 XML 在语法上是正确的,但如果可能的话足够健壮以继续工作。因此,虽然它想查看命名空间,但没有它也可以工作,如错误列表所示。尽管 Nokogiri 标记了问题,但它仍然让我了解标签及其内容。示例代码仍然可以与命名空间声明一起正常工作。这是在使用 Nokogiri 进行解析时使用 CSS 访问器而不是 XPath 的好处之一。
    • @r3nrut,我调整了代码以反映缺少的 XML 命名空间声明。
    猜你喜欢
    • 2020-10-27
    • 1970-01-01
    • 1970-01-01
    • 2015-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    相关资源
    最近更新 更多