seratch's weblog in Japanese

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

libxml-ruby で XML のパース導入編

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

前提条件

libxml2 が必要です。CentOSであれば以下のように準備します。

yum install libxml2-devel

Windows の場合はここから

ftp://ftp.zlatkovic.com/libxml/

「libxml2-2.7.8.win32.zip」を解凍してできた「bin/libxml2.dll」を「libxml2-2.dll」にリネームして「%RUBY_HOME%/bin」に置く。

Ruby 1.9.3p125

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

Ruby Gems

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

sudo gem install libxml-ruby

LibXML::XML::Document

とりあえず XML をパースするだけならこのクラスの使い方を知れば事足りそうです。

#!/usr/bin/env ruby

require 'libxml'

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

doc = XML::Document.string(xml)

実際には「LibXML::XML::Document」というパッケージで libxml.rb の中で include していて「LibXML::」を省略することができています*1

doc = LibXML::XML::Document.string(xml)

XPath を使って XML をパースする

ここでは文字列の XML をパースするので Document.string を使っていますがファイル読み込みの場合は Document.file など他の入力からもパースすることができます。

require 'libxml'

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

doc = LibXML::XML::Document.string(xml)

Document#find に XPath を渡して要素を絞り込みます。

irb(main):018:0> doc.find('/items/item')
=> #<LibXML::XML::XPath::Object:0x1369608>

irb(main):029:0> xpath_object = doc.find('/items/item').is_a?(Enumerable)
=> true


属性は Hash のようにして取得し値は #content で取り出します。
first で最初の要素、 last で最後の要素を取得することもできます。

irb(main):051:0> doc.find('/items/item').each do |item|
irb(main):052:1*   puts item[:id] + ' -> ' + item.content
irb(main):053:1> end
123 -> Andy
234 -> Brian
345 -> Charles
=> #<LibXML::XML::XPath::Object:0x13ddca8>

irb(main):054:0> doc.find('/items/item').first
=> <item id="123">Andy</item>

irb(main):055:0> doc.find('/items/item').last
=> <item id="345">Charles</item>

*1:RubyMine でのソースコードへのジャンプが現状うまくいかないようです。あまり気にするポイントではないと思いますが、もし RubyMine でストレスなくジャンプさせたいとかであれば「LibXML::」を省略せずに指定すれば意図通りになります