Как получить все листовые ячейки из элемента REXML и сохранить их в массив?

Имейте элемент Ruby REXML, как показано ниже:

<a_1>
  <Tests>
    <test enabled='1'>trans </test>
    <test enabled='1'>ac </test>
    <test enabled='1'>dc </test>
  </Tests>
  <Corners>
    <corner enabled='0'>default</corner>
    <corner enabled='1'>C0 </corner>
  </Corners>
</a_1>

Я хочу найти все листовые элементы, поэтому результат должен быть:

<test enabled='1'>trans </test>
<test enabled='1'>ac </test>
<test enabled='1'>dc </test>
<corner enabled='0'>default</corner>
<corner enabled='1'>C0 </corner>

Мой код:

require 'rexml/document' 
include  REXML

def getAllLeaf(xmlElement)
  if xmlElement.has_elements?
    xmlElement.elements.each {|e| 
      getAllLeaf(e)
    }
  else
    return xmlElement
  end
end

Он отлично работает и показывает правильные результаты на экране. Однако я обнаружил, что у меня возникла проблема, когда я пытаюсь сохранить результат в массив для этой рекурсивной процедуры. Так что мне интересно, есть ли способ сохранить этот вывод в один массив, который можно использовать впоследствии?

Я нашел рекурсивный способ сделать это, хотя и немного странный, я хотел бы поделиться им:

def getAllLeaf(eTop,aTemp=Element.new("LeafElements"))
  if eTop.has_elements?
    eTop.elements.each {|e| 
      getAllLeaf(e,aTemp)
    }
  else
    aTemp<< eTop.dup
  end
  return aTemp
end

person user3672656    schedule 24.05.2014    source источник


Ответы (1)


Он отлично работает и показывает правильные результаты на экране.

На самом деле, код нигде не показывает выходных данных. В любом случае ваша рекурсивная функция не работает, в чем вы можете убедиться, если вызовете свой метод для элемента ‹Tests>, когда <Tests> выглядит так:

  <Tests>
    <test enabled='1'>
      <HELLO>world</HELLO>
    </test>
    <test enabled='1'>ac </test>
    <test enabled='1'>dc </test>
  </Tests>

Ваш рекурсивный метод не работает, потому что когда вы пишете:

xmlElement.elements.each {|e|

метод each() возвращает то, что осталось от него, т. е. xmlElement.elements. Учитывая ваш xml, ваш рекурсивный метод эквивалентен:

def getAllLeaf(xmlElement)
    xmlElement.elements.each {|e| 
      "blah"  #your code here has no effect on what each() returns.
    }
end

.. что эквивалентно:

def getAllLeaf(xmlElement)
    return xmlElement.elements
end

Вы хотите придерживаться рекурсии? Гораздо проще искать во всех элементах элементы без дочерних элементов:

require "rexml/document"
include REXML

xml = <<'END_OF_XML'
<a_1>
  <Tests>
    <test enabled='1'>trans </test>
    <test enabled='1'>ac </test>
    <test enabled='1'>dc </test>
  </Tests>
  <Corners>
    <corner enabled='0'>default</corner>
    <corner enabled='1'>C0 </corner>
  </Corners>
</a_1>
END_OF_XML

doc = Document.new xml
root = doc.root

XPath.each(root, "//*") do |element|
  if not element.has_elements?
    enabled = element.attributes['enabled'] 
    text = element.text
    puts "#{enabled} ... #{text}"
  end
end

--output:--
1 ... trans 
1 ... ac 
1 ... dc 
0 ... default
1 ... C0 

Или, если все конечные элементы являются единственными элементами с атрибутом «включен», вы должны сделать это:

XPath.each(root, "//*[@enabled]") do |element|
  enabled = element.attributes['enabled'] 
  text = element.text
  puts "#{enabled} ... #{text}"
end

Есть даже загадочный xpath, который будет напрямую выбирать элементы без дочерних элементов:

XPath.each(root, "//*[not(*)]") do |element|
  enabled = element.attributes['enabled'] 
  text = element.text
  puts "#{enabled} ... #{text}"
end

Кроме того, вы не рассматривали возможность использования драгоценного камня нокогири? Это в значительной степени стандартный синтаксический анализатор XML/HTML для Ruby.

person 7stud    schedule 25.05.2014
comment
Спасибо 7stud, ваше решение довольно хорошее и отлично работает на моей стороне. - person user3672656; 26.05.2014
comment
Спасибо 7stud за ваше объяснение и решение. Решение хорошее и отлично работает на моей стороне. Извините, я новичок в Ruby, только начал использовать REXML и не думал о XPath. Это выглядит действительно мощно, и я думаю, что мне следует изучить его немного больше. - person user3672656; 26.05.2014
comment
Я не знаю, как много вы знаете о парсинге xml, но весь текст заключен в текстовый узел. Это правило относится и к новым строкам. Например, в исходном xml сразу после тега «Тесты» есть новая строка. К сожалению, когда вы проходите через все узлы в документе, текстовые узлы отличаются от элементов, и текстовые узлы вызывают ошибки, такие как \n не имеет метода с именем has_elements? Просто выполните p root.to_a, чтобы увидеть всех прямых потомков root и понять, о чем я говорю. Преимущество Xpath в том, что он извлекает только именованные теги, игнорируя узлы новой строки. - person 7stud; 26.05.2014
comment
Кстати, вы можете заставить REXML пропускать текстовые узлы новой строки без использования XPath, если вы создадите документ следующим образом: doc = Document.new(xml, :ignore_whitespace_nodes=>:all) - person 7stud; 26.05.2014
comment
Привет, 7stud, большое спасибо за подробное объяснение. Да, я получил новую ошибку строки, затем я использовал проверку, чтобы игнорировать ее (опять же нечетная). Мне нужно больше усердно работать над Ruby и REXML...... Еще раз спасибо - person user3672656; 27.05.2014