如何使用Nokogiri访问嵌套的XML

时间:2019-07-17 06:05:02

标签: ruby-on-rails xml nokogiri

我正在使用Nokogiri解析XML。有人告诉我使用CSS选择器来搜索XML,但是无法链接它来访问嵌套对象。

如何访问内部元素?

2.6.3 :039 > pp a.css("interface").to_s          
"<interface>\n" +
"    <status>\n" +
"     <__XML__OPT_Cmd_show_interface_status_down>\n" +
"      <__XML__OPT_Cmd_show_interface_status___readonly__>\n" +
"       <__readonly__>\n" +
"        <TABLE_interface>\n" +
"         <ROW_interface>\n" +
"          <interface>mgmt0</interface>\n" +
"          <state>connected</state>\n" +
"          <vlan>routed</vlan>\n" +
"          <duplex>full</duplex>\n" +
"          <speed>a-1000</speed>\n" +
"          <type>--</type>\n" +
"         </ROW_interface>\n" +
"         <ROW_interface>\n" +
"          <interface>Vlan1</interface>\n" +
"          <state>down</state>\n" +
"          <vlan>routed</vlan>\n" +
"          <duplex>auto</duplex>\n" +
"          <speed>auto</speed>\n" +
"         </ROW_interface>\n" +
"         <ROW_interface>\n" +
"          <interface>Vlan6</interface>\n" +
"          <state>down</state>\n" +
"          <vlan>routed</vlan>\n" +
"          <duplex>auto</duplex>\n" +
"          <speed>auto</speed>\n" +
"         </ROW_interface>\n" +
"         <ROW_interface>\n" +
"          <interface>Vlan486</interface>\n" +
"          <state>down</state>\n" +
"          <vlan>routed</vlan>\n" +
"          <duplex>auto</duplex>\n" +
"          <speed>auto</speed>\n" +
"         </ROW_interface>\n" +
"        </TABLE_interface>\n" +
"       </__readonly__>\n" +
"      </__XML__OPT_Cmd_show_interface_status___readonly__>\n" +
"     </__XML__OPT_Cmd_show_interface_status_down>\n" +
"    </status>\n" +
"   </interface><interface>mgmt0</interface><interface>Vlan1</interface><interface>Vlan6</interface><interface>Vlan486</interface>"

我最终得到了这棵树。我的XPath在这里?这只是解析的XML的一部分:

2.6.3 :043 > pp parsed
#(DocumentFragment:0x3fce080cd300 {
  name = "#document-fragment",
  children = [
    #(ProcessingInstruction:0x3fce080cce14 { name = "xml" }),
    #(Text "\n"),
    #(Element:0x3fce080cc7d4 {
      name = "rpc-reply",
      namespace = #(Namespace:0x3fce080cffb0 {
        prefix = "nf",
        href = "urn:ietf:params:xml:ns:netconf:base:1.0"
        }),
      children = [
        #(Text "\n" + " "),
        #(Element:0x3fce080cf22c {
          name = "data",
          namespace = #(Namespace:0x3fce080cffb0 {
            prefix = "nf",
            href = "urn:ietf:params:xml:ns:netconf:base:1.0"
            }),
          children = [
            #(Text "\n" + "  "),
            #(Element:0x1903f98 {
              name = "show",
              namespace = #(Namespace:0x1903f20 {
                href = "http://www.cisco.com/nxos:1.0:if_manager"
                }),
              children = [
                #(Text "\n" + "   "),
                #(Element:0x1903700 {
                  name = "interface",
                  namespace = #(Namespace:0x1903f20 {
                    href = "http://www.cisco.com/nxos:1.0:if_manager"
                    }),
                  children = [
                    #(Text "\n" + "    "),
                    #(Element:0x19030fc {
                      name = "status",
                      namespace = #(Namespace:0x1903f20 {
                        href = "http://www.cisco.com/nxos:1.0:if_manager"
                        }),
                  children = [
                    #(Text "\n" + "     "),
                    #(Element:0x1902a1c {
                      name = "__XML__OPT_Cmd_show_interface_status_down",
                      namespace = #(Namespace:0x1903f20 {
                        href = "http://www.cisco.com/nxos:1.0:if_manager"
                        }),

2 个答案:

答案 0 :(得分:1)

您的问题确实很笼统,​​很难回答,因此不可能回答一个特定的问题,但是您似乎需要了解如何使用CSS访问器访问文档中的标签,这使Nokogiri非常容易。

对此进行冥想:

require 'nokogiri'

foo =<<EOT
<tag1>
  <tag2>some text</tag2>
  <tag3>some more text</tag3>
  <tags>something</tags>
  <tags>or</tags>
  <tags>other</tags>
</tag1>
EOT

xml = Nokogiri::XML.parse(foo)

at在文档中找到第一个匹配项:

xml.at('tag2').content # => "some text"

at非常聪明,它会尝试确定访问器是CSS还是XPath,因此当您想要 first 匹配时,它是一个很好的首选工具。如果那不起作用,那么您可以尝试使用at_css来指定访问器是CSS,因为有时您会想到一些可以作为CSS或XPath使用但返回不同结果的东西:

xml.at_css('tag3').content # => "some more text"
xml.at_css('tag3').text # => "some more text"

at类似的是search,它还会尝试确定它是CSS还是XPath,但会发现整个文档中所有匹配的节点,而不仅仅是第一个匹配的节点。因为它返回所有匹配的节点,所以它返回一个NodeSet,而不像at返回一个Node,因此您必须知道,NodeSet在访问其contenttext时的行为不同于Node:

xml.search('tags').text # => "somethingorother"

这几乎永远不是您想要的,但是您会惊讶地发现有这么多人问如何将结果字符串拆分为所需的三个单词。通常不可能准确地做到这一点,因此需要一种不同的策略:

xml.search('tags').map { |t| t.content } # => ["something", "or", "other"]
xml.search('tags').map { |t| t.text } # => ["something", "or", "other"]
xml.search('tags').map(&:text) # => ["something", "or", "other"]

atsearch都有..._css..._xpath的变体,以帮助您微调代码的行为,但我始终建议从通用的{{1}开始}和at,直到您被迫定义访问器是什么。

我还建议从XPath开始使用CSS访问器,因为如果您正在使用CSS编写HTML,它们通常更易读,更容易学习。 XPath非常强大,可能比CSS还要强大,但是学习XPath会花费更长的时间,并且通常会导致代码的可读性降低,从而影响可维护性。

这全部在tutorials and cheat sheetsdocumentation中。 Nokogiri功能非常强大,但是需要花费一些时间阅读并尝试学习。您也可以search on SO来完成我写的有关搜索XML和HTML文档的其他内容;特别是“ What are some examples of using Nokogiri?”有助于您了解如何抓取页面。有很多信息涉及与此相关的许多不同主题。我发现解析这样的文档是一项有趣的练习,因为这是我多年职业生涯的一部分。

答案 1 :(得分:0)

您可以使用xpath

parsed = Nokogiri::XML::DocumentFragment.parse(xml)
siamese_cat = parsed.xpath(.//interface/status/state)

或者只是通过XML进行迭代

parsed = Nokogiri::XML::DocumentFragment.parse(xml)
parsed.each do |element|
   # Some instructions
end