読者です 読者をやめる 読者になる 読者になる

REXML と Libxml-Ruby とのエンティティ出力比較

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

テストしたコード

#!/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; <>"'