处理存储具有相同名称和不同XPath的子元素?

时间:2014-05-16 00:43:43

标签: ruby xpath nokogiri

我试图用Nokogiri从XML中提取值。

我想在数组中分隔具有相同名称但不同xpath的子元素。这些元素是ProdAProdB

目前我只是尝试打印子元素,但到目前为止我的代码只打印了#34; 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,只能得到唯一的XPath。

例如:

子元素StNameStCode每个元素只有一个出现,那么到目前为止具有XPath的数组将是:

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

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

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

作为节点ProdA的子节点的Items_B节点具有以下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]是包含ProdB所有Items_B个节点子项的值的数组。

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:

锡人你好,它有效!这是什么意思&#34;%w&#34;和&#34;%w [element1 element2]&#34;?表单%w [...]是否接受超过2个元素?

我是Nokogiri的新手,我只提到Xpath,因为XML有200多个独特的子节点(独特的Xpath&#39; s),那么你建议我对所有子节点使用相同的CSS技术或者是否存在一种处理XML并执行相同操作的方法(在数组中将具有相同名称且具有相同Xpath的元素分组),而不知道子节点的名称?我想知道你建议我的方式。

再次感谢

1 个答案:

答案 0 :(得分:0)

这是一种方式:

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中会有多个<SDocument>标签。如果没有,则可以稍微修改代码以返回数组,如您所知:

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

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

注意我正在使用CSS选择器,以便于指定我希望代码查看两个不同的节点,包括Items_AItems_B,以及ProdA和{ {1}}。


问题彻底改变后更新:

这是设置:

ProdB

以下是代码:

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