【问题标题】:Handle storing of child elements with same name and different XPath?处理具有相同名称和不同 XPath 的子元素的存储?
【发布时间】:2014-07-04 15:35:00
【问题描述】:

我正在尝试使用 Nokogiri 从 XML 中提取值。

我想将具有相同名称但不同 xpath 的子元素存储在一个数组中。这些元素是ProdAProdB

目前我只尝试打印子元素,但到目前为止我的代码只打印“SDocument”而不是子元素。

目标是有一个这样的数组:

array = [["2","8"], ["8","9"]]

这是代码:

#!/usr/bin/env ruby
require 'nokogiri'

doc = Nokogiri::XML(File.open("input.xml"))

a = doc.xpath("//SDocument").each do |n|
  n if n.text?
end

puts a

这是 XML:

<?xml version="1.0" encoding="UTF-8"?>
<Document-St-5>
  <SDocument>
    <ItemList>
      <Items_A>
        <ItemElem>
          <Item_Values>
            <ProdA>2</ProdA>
            <ProdB>8</ProdB>
          </Item_Values>
        </ItemElem>        
      </Items_A>
      <Items_B>
        <ItemElem>
          <Item_Values>
            <ProdA>8</ProdA>
            <ProdB>9</ProdB>
          </Item_Values>
        </ItemElem>
      </Items_B>
    </ItemList>
  </SDocument>
</Document-St-5>

有人可以指点我正确的方法吗?


更新:

我真正想要的是在一个数组中存储 SDocument 节点的所有唯一子元素的 XPath 以及具有多个 发生,将它们分组存储。但如果可能在不知道孩子名称的情况下获取 XPath,则只获取唯一的 XPath。

例如:

子元素StNameStCode 各只出现一次,那么目前有XPath 的数组将是:

arr_Xpath = [ ["/Document-St-5/SDocument/StName"], ["/Document-St-5/SDocument/StCode"], ... ]

作为节点 Items_A 子节点的 ProdA 节点具有以下 XPath:

/Document-St-5/SDocument/ItemList/Items_A/ItemElem/Item_Values/ProdA

作为节点 Items_B 子节点的 ProdA 节点具有以下 XPath:

/Document-St-5/SDocument/ItemList/Items_B/ItemElem/Item_Values/ProdA

那么子元素的唯一 XPath 数组将是(包括 ProdB 节点的 XPath):

arr_Xpath = [ "/Document-St-5/SDocument/StName", 
        "/Document-St-5/SDocument/StCode", 
        "/Document-St-5/SDocument/ItemList/Items_A/ItemElem/Item_Values/ProdA", 
        "/Document-St-5/SDocument/ItemList/Items_A/ItemElem/Item_Values/ProdB",
        "/Document-St-5/SDocument/ItemList/Items_B/ItemElem/Item_Values/ProdA",
                  "/Document-St-5/SDocument/ItemList/Items_B/ItemElem/Item_Values/ProdB" ]

我认为,首先了解唯一的 XPath,可以使用 doc.xpath("..") 获取每个子元素的值并将它们分组 如果它不止一次出现。所以,我想得到的最终数组是:

arr_Values = [ ["WERLJ01"], ["MEKLD"],["2","9"],["8","3"],["1"],["17"]]

地点:

  • arr_Values[0] 是包含 StName 值的数组
  • arr_Values[1] 是包含 StCode 值的数组
  • arr_Values[2] 是包含 ProdA 节点的所有 Items_A 子节点值的数组。
  • arr_Values[3] 是包含 ProdB 节点的所有 Items_A 子节点值的数组。
  • arr_Values[4] 是包含 ProdA 节点的所有 Items_B 子节点的值的数组。
  • arr_Values[5] 是包含 Items_B 的所有 ProdB 节点的子节点值的数组。

一个 XML 示例是:

<?xml version="1.0" encoding="UTF-8"?>
<Document-St-5>
  <SDocument>
    <StName>WERLJ01</StName>
    <StCode>MEKLD</StCode>
  <ItemList>
    <Items_A>
      <ItemElem>
        <Item_Values>
          <ProdA>2</ProdA>
          <ProdB>8</ProdB>
        </Item_Values>
      </ItemElem>        
    </Items_A>
    <Items_A>
      <ItemElem>
        <Item_Values>
          <ProdA>9</ProdA>
          <ProdB>3</ProdB>
        </Item_Values>
      </ItemElem>        
    </Items_A>       
    <Items_B>
      <ItemElem>
        <Item_Values>
          <ProdA>1</ProdA>
          <ProdB>17</ProdB>
        </Item_Values>
      </ItemElem>
    </Items_B>
  </ItemList>
  </SDocument>
</Document-St-5>  

更新 2:

你好,铁皮人,它有效! “%w”和“%w[element1 element2]”是什么意思? %w[...] 表单是否接受超过 2 个元素?

我是 Nokogiri 的新手,我只提到 Xpath,因为 XML 有 200 多个唯一的子节点(唯一的 Xpath),那么你建议我对所有子节点使用与 CSS 相同的技术还是有办法处理XML 并在不知道子节点名称的情况下执行相同操作(将具有相同名称和相同 Xpath 的元素分组到数组中)?我想知道你建议我的方式。

再次感谢

【问题讨论】:

  • 人们/回答者使用 XPaths 还是 CSS 作为访问器有什么区别? XPaths,尤其是您展示过的 XPaths,将不那么灵活而且更冗长。指定节点的完整路径也会让你的代码更加脆弱;对 XML 结构的更改会破坏您的应用程序。相反,使用地标来定位您想要的数据。

标签: ruby xpath nokogiri


【解决方案1】:

这是一种方法:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<?xml version="1.0" encoding="UTF-8"?>
<Document-St-5>
  <SDocument>
    <ItemList>
      <Items_A>
        <ItemElem>
          <Item_Values>
            <ProdA>2</ProdA>
            <ProdB>8</ProdB>
          </Item_Values>
        </ItemElem>        
      </Items_A>
      <Items_B>
        <ItemElem>
          <Item_Values>
            <ProdA>8</ProdA>
            <ProdB>9</ProdB>
          </Item_Values>
        </ItemElem>
      </Items_B>
    </ItemList>
  </SDocument>
</Document-St-5>
EOT

data = doc.search('SDocument').map{ |node| 
  %w[ProdA ProdB].map{ |n| node.search(n).map(&:text) }
}


data # => [[["2", "8"], ["8", "9"]]]

这会导致嵌套比你想要的更深,但它很接近。

有点不同的方式,也许更容易理解,是:

data = doc.search('SDocument').map{ |node| 
  %w[A B].map{ |ab|
    node.at("Items_#{ ab }").search('ProdA, ProdB').map(&:text)
  }
}

嵌套比您指定的更深一层的原因是,我假设 XML 中会有多个 &lt;SDocument&gt; 标记。如果没有,则可以稍微修改代码以按照您的要求返回数组:

data = doc.search('Items_A, Items_B').map{ |node| 
  node.search('ProdA, ProdB').map(&:text)
}

data # => [["2", "8"], ["8", "9"]]

请注意,我正在使用 CSS 选择器,以便于指定我希望代码查看两个不同的节点,分别是 Items_AItems_B,以及 ProdAProdB


问题完全改变后更新:

设置如下:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<?xml version="1.0" encoding="UTF-8"?>
<Document-St-5>
  <SDocument>
    <StName>WERLJ01</StName>
    <StCode>MEKLD</StCode>
  <ItemList>
    <Items_A>
      <ItemElem>
        <Item_Values>
          <ProdA>2</ProdA>
          <ProdB>8</ProdB>
        </Item_Values>
      </ItemElem>        
    </Items_A>
    <Items_A>
      <ItemElem>
        <Item_Values>
          <ProdA>9</ProdA>
          <ProdB>3</ProdB>
        </Item_Values>
      </ItemElem>        
    </Items_A>       
    <Items_B>
      <ItemElem>
        <Item_Values>
          <ProdA>1</ProdA>
          <ProdB>17</ProdB>
        </Item_Values>
      </ItemElem>
    </Items_B>
  </ItemList>
  </SDocument>
</Document-St-5>  
EOT

代码如下:

data = %w[StName StCode].map{ |n| [doc.at(n).text] }
%w[ProdA ProdB].each do |prod|
  data << doc.search('Items_A').map{ |item| item.at(prod).text }
end
%w[ProdA ProdB].each do |prod|
  data << [doc.at("Items_B #{prod}").text]
end

捕获的内容如下:

data # => [["WERLJ01"], ["MEKLD"], ["2", "9"], ["8", "3"], ["1"], ["17"]]

【讨论】:

  • 您好,铁皮人,感谢您分享的答案和解释。请参阅“更新...”下面的问题,我已经更新了它,因为我的第一个解释是不准确的,我希望你能指出我正确的方向以使其成为可能。提前致谢。
  • 您的问题在被问到时要准确,这一点真的很重要。稍后通过提供不同的源数据或不同的要求对其进行更改,可能会完全改变答案,从而导致其他人在搜索相同问题时会混淆 Q&A 页面。此外,它会让你更难得到你想要的答案。
  • 是的,这是一个错误,如果可以接受,我会更新。如果不是,我不确定是否最好打开一个新问题?
  • 你好,铁皮人,它有效! “%w”和“%w[element1 element2]”是什么意思? %w[...] 表单是否接受超过 2 个元素?
  • 由于 XML 有 200 多个唯一的子节点(唯一的 Xpath),您是否建议我对 CSS 使用相同的技术,或者有没有办法处理 XML 并执行相同的操作(数组中的组具有相同名称和相同 Xpath 的元素)不知道子节点的名称?我想知道你建议我的方式。再次感谢
猜你喜欢
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多