Приведение элементов в порядок с REXML XPath - PullRequest
3 голосов
/ 15 ноября 2010

Я бы хотел перебрать все элементы <HeadA> и <HeadB> в XML-файле и добавить уникальный идентификатор для каждого из них.Подход, который я пробовал до сих пор:

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

Проблема в том, что набор узлов из XPath //HeadA | //HeadB - это все HeadA с, за которыми следуют все HeadB с.Что мне нужно, так это упорядоченный список всех HeadA и HeadB 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>

, чтобы я мог добавитьидентификаторы последовательно.

Ответы [ 4 ]

1 голос
/ 15 ноября 2010

Использование 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 и почти закончить. Просто добавьте код, чтобы вставить уникальный идентификатор.

1 голос
/ 15 ноября 2010

Хорошо, 2-я попытка, но я думаю, что у меня получилось на этот раз: P

@xml.each_element('//*[self::HeadA or self::HeadB]') do |heading|
  puts heading.text
end
0 голосов
/ 15 ноября 2010

Я нашел быстрое и грязное решение:

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

Возможно, оно не самое красивое и не самое эффективное, но оно делает то, что я хотел.

Редактировать: Принимая совет ДД-Дага, я теперь получил это:

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

, что НАМНОГО лучше.

0 голосов
/ 15 ноября 2010

Будет ли это работать для вас, если вы пройдете через всю 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
...