首页 文章

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

提问于
浏览
0

我正试图用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 node的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] 是包含 Items_A 的所有 ProdA 节点子节点的值的数组 .

  • arr_Values[3] 是包含 Items_A 的所有 ProdB 节点子节点的值的数组 .

  • arr_Values[4] 是包含 Items_B 的所有 ProdA 节点子节点的值的数组 .

  • 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>

Update 2:

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

我是Nokogiri的新手,我只提到Xpath,因为XML有超过200个独特的子节点(独特的Xpath),那么你建议我对所有子节点使用相同的CSS技术,或者有办法处理XML和在不知道子节点名称的情况下,做同样的事情(数组中具有相同名称且具有相同Xpath的元素)?我想知道你建议我的方式 .

再次感谢

1 回答

  • 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> 标记 . 如果有赢得't be, then the code can be modified a bit to return the array as you'问:

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

相关问题