REXML で XML を解析し Hash に変換する
REXML は Ruby に標準でついてくる XMLパーサです.XML な文字列を食わせるとパースして REXML::Document として利用できます.
しかし,REXML のドキュメントを見て扱いを知らなければならないので,Hash で取りたいこともあると思います.実際 ActiveSupport の core_ext には Hash.from_xml が定義されています.
from_xml のためだけに ActiveSupport を require するのも億劫だったので,簡単に実装してみました.
class Hash class << self def from_xml(rexml) xml_elem_to_hash rexml.root end private def xml_elem_to_hash(elem) value = if elem.has_elements? children = {} elem.each_element do |e| children.merge!(xml_elem_to_hash(e)) do |k,v1,v2| v1.class == Array ? v1 << v2 : [v1,v2] end end children else elem.text end { elem.name.to_sym => value } end end end
REXML::Document を受け取って パースされた XML を Hash に落とします.
xml = <<"XML" <All> <WordList> <Word>foo</Word> <Word>bar</Word> </WordList> <Hoge></Hoge> </All> XML doc = REXML::Document.new(xml) Hash.from_xml(doc) == {:All=>{:WordList=>{:Word=>["foo", "bar"]}, :Hoge=>nil}} #=> true
こんな感じに Hash に変換してくれます.
XML を Hash に落とす上で
の2つの問題があります.
1つめは再帰を使えば簡単に解決できますが,2つ目は若干厄介です.
Hash#merge ではブロックでキー衝突時の処理を指定できるので,衝突時に Array にして詰め込むようにしています.