代码之家  ›  专栏  ›  技术社区  ›  Skilldrick

使用REXML XPath按顺序获取元素

  •  3
  • Skilldrick  · 技术社区  · 14 年前

    我想把所有的 <HeadA> <HeadB> XML文件中的元素,并为每个元素添加唯一的id。到目前为止,我尝试的方法是:

    @xml.each_element('//HeadA | //HeadB') do |heading|
      #add a new id
    end
    

    问题是,XPath中的nodeset //HeadA | //HeadB 是所有的 HeadA 然后是所有的 HeadB s、 我需要的是 黑达 s和 人头 按它们在文档中出现的顺序排列。

    为了澄清这一点,我的XML可以如下所示:

    <Doc>
      <HeadA>First HeadA</HeadA>
      <HeadB>First HeadB</HeadB>
      <HeadA>Second HeadA</HeadA>
      <HeadB>Second HeadB</HeadB>
    </Doc>
    

    我从XPath中得到的是:

      <HeadA>First HeadA</HeadA>
      <HeadA>Second HeadA</HeadA>
      <HeadB>First HeadB</HeadB>
      <HeadB>Second HeadB</HeadB>
    

    当我需要得到的是按顺序排列的节点时:

      <HeadA>First HeadA</HeadA>
      <HeadB>First HeadB</HeadB>
      <HeadA>Second HeadA</HeadA>
      <HeadB>Second HeadB</HeadB>
    

    所以我可以按顺序添加id。

    4 回复  |  直到 14 年前
        1
  •  1
  •   Doug    14 年前

    好的,第二次试试,但我想这次我有了:P

    @xml.each_element('//*[self::HeadA or self::HeadB]') do |heading|
      puts heading.text
    end
    
        2
  •  1
  •   Skilldrick    14 年前

    使用Nokogiri解析XML:

    xml = %q{
    <Doc>
        <HeadA>First HeadA</HeadA>
        <HeadB>First HeadB</HeadB>
        <HeadA>Second HeadA</HeadA>
        <HeadB>Second HeadB</HeadB>
    </Doc>
    }
    
    doc = Nokogiri::XML(xml)
    doc.search('//HeadA | //HeadB').map{ |n| n.inner_text } #=> ["First HeadA", "First HeadB", "Second HeadA", "Second HeadB"]
    

    对于您的任务,您可以替换 map 具有 each each_with_index 就快完成了。只需添加代码插入唯一的ID。

        3
  •  0
  •   Doug    14 年前

    如果你把所有的头A都圈起来,在每个头A里,把每个头B都圈起来,对你有用吗?

    @xml.each_element("//HeadA") do |headA|
      #do stuff to headA
      headA.each_element("HeadB") do |headB|
        #do stuff to headB
      end
    end
    
        4
  •  0
  •   Skilldrick    14 年前

    我想出了一个快速而肮脏的解决方案:

    as_string = @xml.to_s
    counter = 0
    as_string.gsub!(/(<HeadA>|<HeadB>)/) do |str|
      result = str.sub '>', " id='#{counter}'>"
      counter += 1
      result
    end
    @xml = REXML::Document.new as_string
    

    它可能不是最漂亮或最有效的,但它做到了我想要它做的。

    编辑: 听了D-D-Doug的建议,我现在明白了:

    counter = 0
    @xml.each_element('//[self::HeadA or self::HeadB]') do |heading|
      heading.attributes['id'] = "id%03d" % counter
      counter += 1
    end
    

    那就好多了。