seratch's weblog in Japanese

About Scala, Java and Ruby programming in Japaense. If you need English information, go to http://blog.seratch.net/

ActiveSupport で XML のパース導入編

RubyXML をパースしたい」ということで(蛇足気味ですが)調べたメモを公開します。

ActiveSupport でやるのが簡単でいいですね。

前提条件

Hash.from_xml や XmlMini をデフォルトで使う

標準ライブラリの REXML を使うので、特にありません。

XmlMini.backend = 'LibXML' で使う

libxml2 が必要です。

http://d.hatena.ne.jp/seratch2/20120305/1330931370

XmlMini.backend = 'Nokogiri' で使う

libxml2 と libxslt が必要です。

http://d.hatena.ne.jp/seratch2/20120305/1330931371

Ruby 1.9.3p125

Ruby 1.9.3p125 の環境で試しています。

Ruby Gems

Windows の場合は sudo は要りません。

Hash.from_xml や XmlMini をデフォルトで使う
sudo gem install activesupport
XmlMini.backend = 'LibXML' で使う
sudo gem install libxml-ruby
sudo gem install activesupport
XmlMini.backend = 'Nokogiri' で使う
sudo gem install nokogiri
sudo gem install activesupport

XML をパースして Hash オブジェクトにする

デフォルトのまま
require 'active_support'

xml = <<EOM
<?xml version="1.0" encoding="UTF-8"?>
<items>
  <item id="123">
    <name>Andy</name>
    <age>21</age>
  </item>
  <item id="234">
    <name>Brian</name>
    <age>23</age>
  </item>
  <item id="345">
    <name>Charles</name>
    <age>19</age>
  </item>
</items>
EOM

doc = ActiveSupport::XmlMini.parse(xml)
doc.is_a?(Hash) # => true

doc はこんな感じの Hash になります。

{"items" =>
  {"item" => [
    {"id" => "123", "name" => {"__content__" => "Andy"}, "age" => {"__content__" => "21"}},
    {"id" => "234", "name" => {"__content__" => "Brian"}, "age" => {"__content__" => "23"}},
    {"id" => "345", "name" => {"__content__" => "Charles"}, "age" => {"__content__" => "19"}}
  ]}
}

階層が深くなってしまいます。

以下のリンク先にあるような問題を考慮しなくてよい場合は

http://techracho.bpsinc.jp/baba/2010_05_25/1597

Hash.from_xml を使う方が便利です。

ソースコードを読むと分かるとおり、内部的に XmlMini.parse を呼んでさらに結果の Hash を加工しています。

https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/hash/conversions.rb

require 'active_support/core_ext'
doc = Hash.from_xml(xml)

結果はこのようになります。属性の id も小要素の name や age も同じように Hash に格納されます。

{"items" =>
  {"item" => [
    {"id"=>"123", "name"=>"Andy", "age"=>"21"}, 
    {"id"=>"234", "name"=>"Brian", "age"=>"23"}, 
    {"id"=>"345", "name"=>"Charles", "age"=>"19"}
  ]}
}
ActiveSupport::XmlMini.backend = 'LibXML'

使い方は何も変わりません。内部的に LibXML Ruby が使われて高速になります。

require 'active_support'
ActiveSupport::XmlMini.backend = 'LibXML'
doc = ActiveSupport::XmlMini.parse(xml)

require 'active_support/core_ext'
doc = Hash.from_xml(xml)
ActiveSupport::XmlMini.backend = 'Nokogiri'

使う側は何も変わりません。内部的に Nokogiri が使われて高速になります。

require 'active_support'
ActiveSupport::XmlMini.backend = 'Nokogiri'
doc = ActiveSupport::XmlMini.parse(xml)

require 'active_support/core_ext'
doc = Hash.from_xml(xml)