はじめに

RubyのXML処理のする際に、REXMLライブラリのオプションを使うだけでは、空白や改行を削除できませんでした。

そこで、Rubyが得意とするメタプログラミングにより、モンキーパッチを作成してメソッドを上書きさせることで実現します


環境

ruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-linux]

標準のオプションで実施した場合

compress_whitespace オプションをりようすればある程度削除されますが、
空白が1つは残ります。

コード

空白文字がわかるように 表示の際に $end を追加してます
require 'rexml/document'

def print_doc(doc)
  doc.get_elements("/xbel").each{|tags|
    i = 0
    tags.get_elements("tag").each{|tag|
      i+=1
      puts "[ tag" + i.to_s + " ]"
      puts tag.get_text.to_s + "$end"
    }
  }
end
xml_doc=<<'XML'
<?xml version="1.0" encoding="UTF-8" ?>
<xbel version="1.0">
  <tag>aaa</tag>
  <tag>     bbb    </tag>
  <tag>   c  c  c     c     c   </tag>
  <tag>a
a
a  a  a
a     a     a
a
</tag>
  <tag> bbb
 b b b
b
b     </tag>
</xbel>
XML
puts "###############################################"
puts "NORMAL"
puts "###############################################"
doc = REXML::Document.new(xml_doc)
print_doc(doc)


puts "###############################################"
puts "compress_whitespace"
puts "###############################################"
doc1 = REXML::Document.new(xml_doc,{:compress_whitespace => :all})


実行結果

###############################################
NORMAL
###############################################
[ tag1 ]
aaa$end
[ tag2 ]
     bbb    $end
[ tag3 ]
   c  c  c     c     c   $end
[ tag4 ]
a
a
a  a  a
a     a     a
a
$end
[ tag5 ]
 bbb
 b b b
b
b     $end



############################################### compress_whitespace ############################################### [ tag1 ] aaa$end [ tag2 ] bbb $end [ tag3 ] c c c c c $end [ tag4 ] a a a a a a a a a $end [ tag5 ] bbb b b b b b $end


モンキーパッチによる実装をした場合

REXML::Element クラスに対するモンキーパッチを作成して既存のメソッドを上書きしてます

コード

require 'rexml/document'

def print_doc(doc)
  doc.get_elements("/xbel").each{|tags|
    i = 0
    tags.get_elements("tag").each{|tag|
      i+=1
      puts "[ tag" + i.to_s + " ]"
      puts tag.get_text.to_s + "$end"
    }
  }
end

class REXML::Element
  alias_method :__get_text__, :get_text
  def get_text(*path)
    __get_text__(*path).to_s.gsub(/[ \n]/,"")
  end
  private :__get_text__
end

xml_doc=<<'XML'
<?xml version="1.0" encoding="UTF-8" ?>
<xbel version="1.0">
  <tag>aaa</tag>
  <tag>     bbb    </tag>
  <tag>   c  c  c     c     c   </tag>
  <tag>a
a
a  a  a
a     a     a
a
</tag>
  <tag> bbb
 b b b
b
b     </tag>
</xbel>
XML
puts "###############################################"
puts "Monkey Patch"
puts "###############################################"
doc = REXML::Document.new(xml_doc)
print_doc(doc)

実行結果

###############################################
Monkey Patch
###############################################
[ tag1 ]
aaa$end
[ tag2 ]
bbb$end
[ tag3 ]
ccccc$end
[ tag4 ]
aaaaaaaaa$end
[ tag5 ]
bbbbbbbb$end

うまくいきました。

ちなみにXMLドキュメントそのものに対して、正規表現で置換処理をさせることも可能ですが、
XMLドキュメントが多くなるとクソほど遅くなるのでおすすめしません。