首页 > 解决方案 > 如何使用 Nokogiri 访问嵌套的 XML

问题描述

我正在使用 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"
                        }),

标签: ruby-on-railsrubyxmlnokogiri

解决方案


你的问题真的很笼统,而且问得不好,所以不可能回答一个特定的问题,但看起来你需要了解如何使用 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,因此当您想要第一次匹配时,它是一个很好的第一个工具。如果这不起作用,那么您可以尝试at_css指定访问器是 CSS,因为有时您可以想出一些可以用作 CSS 或 XPath 但返回不同结果的东西:

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

at与is类似search,它也尝试确定它是 CSS 还是 XPath,但会查找整个文档中的所有匹配节点,而不仅仅是第一个匹配的节点。因为它返回所有匹配的节点,所以它返回一个 NodeSet,不像at返回一个 Node,所以你必须知道 NodeSet 在访问它们的contentor时的行为与 Nodes 不同text

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

atsearchhave..._css和变体都..._xpath可以帮助您微调代码的行为,但我始终建议从泛型开始atsearch直到您被迫定义访问器是什么。

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

这些都在教程、备忘单文档中。Nokogiri 非常强大,但需要时间阅读和尝试才能学习它。您还可以在 SO 上搜索我写的关于搜索 XML 和 HTML 文档的其他内容;特别是“哪些是使用 Nokogiri 的示例? ”有助于了解如何抓取页面。有很多信息涉及与此相关的许多不同主题。我发现解析这样的文档是一项有趣的练习,因为它多年来一直是我职业生涯的一部分。


推荐阅读