【问题标题】:Getting elements in order with REXML XPath使用 REXML XPath 按顺序获取元素
【发布时间】:2010-11-15 13:13:12
【问题描述】:

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

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

问题是,来自 XPath //HeadA | //HeadB 的节点集是所有 HeadAs 后跟所有 HeadBs。我需要的是所有HeadAs 和HeadBs 的有序列表,按照它们在文档中出现的顺序排列。

澄清一下,我的 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。

【问题讨论】:

  • 任何兼容的 XPath 引擎都必须按文档顺序选择节点。你的显然是不合规的。强烈建议不要使用它,不要误认为这是XPath。
  • @Dimitre 谢谢,很高兴知道。
  • @Dimitre:事实上,没有规范强制执行结果节点集的顺序。这是托管语言的责任。你说得对,几乎每个 XPath 引擎都会使用文档顺序。

标签: ruby xpath rexml


【解决方案1】:

好的,第二次尝试,但我想这次我成功了:P

@xml.each_element('//*[self::HeadA or self::HeadB]') do |heading|
  puts heading.text
end

【讨论】:

  • 做到了!我已经成功地将我原来的 8 条粗糙线条变成了 5 条美丽而清晰的线条。谢谢:)
【解决方案2】:

使用 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。

【讨论】:

  • 谢谢。我以前没有使用过 Nokogiri,但这看起来是一种不错的 Rubyish 技术。
  • Nokogiri 用于 XML 和 HTML 解析。它特别酷的地方在于,您实际上可以使用更简单的 CSS 访问器进行大量 XML 查找。
【解决方案3】:

如果您循环遍历所有 HeadA 并在每个 HeadA 内循环遍历每个 HeadB,它对您有用吗?

@xml.each_element("//HeadA") do |headA|
  #do stuff to headA
  headA.each_element("HeadB") do |headB|
    #do stuff to headB
  end
end

【讨论】:

  • 不,它们不是嵌套的。不过还是谢谢。
【解决方案4】:

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

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

这样更好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-09
    • 1970-01-01
    相关资源
    最近更新 更多