REXML で大きな XML ファイルを処理したらあまりにも遅かった。Ruby で書かれているのでいたし方ないんですが。もちろん REXML は標準でついているし、手軽に使えて良いという大きなメリットがあるけど、行う処理が数時間レベルなので速くしたい。そこで Libxml http://libxml.rubyforge.org/ を試すことに。
行うのは XML → XML 変換なので、エンティティの出力が気になった。そこで次のようなコードを実行して、結果を比較してみる。

テストしたコード

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/usr/bin/env ruby
#
require 'rubygems'
require 'xml/libxml'
require 'rexml/document'
str =<<EOS
<?xml version="1.0" encoding="UTF-8"?>
<root>
 <title>Tosshi&apos;s メモ書き</title>
 <array>
 <item attr='&apos;'> &lt;html&gt; &apos;TAG&quot; </item>
 <item attr="&quot;"><!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]></item>
 </array>
</root>
EOS
puts <<EOS
\[ libxml-ruby \]
EOS
xp = XML::Parser.new
xp.string = str
libxmldoc = xp.parse
puts "document :"
puts libxmldoc
node = libxmldoc.root.find_first("title")
print "Node      :"
puts node
print "content   :"
puts node.content
print "child.to_s:"
puts node.child.to_s
libxmldoc.root.find("array/item").each do |item|
print "Node      :"
puts item
print "content   :"
puts item.content
print "child.to_s:"
puts item.child.to_s
end
puts <<EOS
\[ REXML \]
EOS
rexmldoc = REXML::Document.new(str)
puts "Document :"
puts rexmldoc
el = rexmldoc.root.elements\["title"\]
print "Element   :"
puts el
print "text      :"
puts el.text
print "get_text  :"
puts el.get_text
rexmldoc.root.elements.each("array/item") do |item|
print "Element   :"
puts item
print "text      :"
puts item.text
print "get_text  :"
puts item.get_text
end

結果

  • libxml はアポストロフィとダブルクォートを必要なときだけ実体名にエスケープする。
  • REXML は属性値は固定でアポストロフィで囲い、アポストロフィとダブルクォートは必ず実体名にエスケープする。
$ ruby testxml.rb
\[ libxml-ruby \]
Document :
<?xml version="1.0" encoding="UTF-8"?>
<root>
<title>Tosshi's メモ書き</title>
<array>
<item attr="'"> &lt;html&gt; 'TAG" </item>
<item attr="&quot;"><!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]></item>
</array>
</root>
Node      :<title>Tosshi's メモ書き</title>
content   :Tosshi's メモ書き
child.to_s:Tosshi's メモ書き
Node      :<item attr="'"> &lt;html&gt; 'TAG" </item>
content   : <html> 'TAG"
child.to_s: &lt;html&gt; 'TAG"
Node      :<item attr="&quot;"><!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]></item>
content   : CDATA &lt;html&gt; &apos;TAG&qout; <>"'
child.to_s:<!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]>
\[ REXML \]
Document :
<?xml version='1.0' encoding='UTF-8'?>
<root>
<title>Tosshi&apos;s メモ書き</title>
<array>
<item attr='&apos;'> &lt;html&gt; &apos;TAG&quot; </item>
<item attr='&quot;'><!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]></item>
</array>
</root>
Element   :<title>Tosshi&apos;s メモ書き</title>
text      :Tosshi's メモ書き
get_text  :Tosshi&apos;s メモ書き
Element   :<item attr='&apos;'> &lt;html&gt; &apos;TAG&quot; </item>
text      : <html> 'TAG"
get_text  : &lt;html&gt; &apos;TAG&quot;
Element   :<item attr='&quot;'><!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]></item>
text      : CDATA &lt;html&gt; &apos;TAG&qout; <>"'
get_text  : CDATA &lt;html&gt; &apos;TAG&qout; <>"'