ruby - ライブラリ - rexml/document ライブラリ

 
1. 概要
2. 単純に読んで出力してみる
3. 要素を取得する
4. 要素を更新する

1. 概要

 xml ファイルを扱ってみようと思いましたら、組込のライブラリにこれがあるそうで・・・。  本項は下記のサイトを参考にさせていただきました。
library rexml/document (Ruby 2.5.0)」
「RubyからXMLファイルを動的に編集する - Qiita

2. 単純に読んで出力してみる

 参考サイトのまま(少し訂正すべき個所がありましたが)に書いてみました。  こういう内容の(前半のみ掲載)xml ファイルがあります。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE urlset>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://freebsd.sing.ne.jp/</loc>				<lastmod>2018-10-10</lastmod><changefreq>monthly</changefreq><priority>0.3</priority></url>		<!-- トップ 	-->
<url><loc>https://freebsd.sing.ne.jp/00/</loc>			<lastmod>2018-10-30</lastmod><changefreq>monthly</changefreq><priority>0.1</priority></url>		<!-- 共通		-->

<url><loc>https://freebsd.sing.ne.jp/01/08.04/</loc>	<lastmod>2015-06-30</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>		<!-- 8.4 RELEASE -->
<url><loc>https://freebsd.sing.ne.jp/01/09.03</loc>		<lastmod>2016-12-31</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>		<!-- 9.3 RELEASE -->
<url><loc>https://freebsd.sing.ne.jp/01/10.02/</loc>	<lastmod>2016-12-31</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>		<!-- 10.2 RELEASE -->
<url><loc>https://freebsd.sing.ne.jp/01/11.00/</loc>	<lastmod>2017-11-30</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>		<!-- 11.0 RELEASE -->
 入力したファイルをそのまま出力したらどうなるのかをやってみました。
require('pry')
require('rexml/document')

Dir.chdir(File.dirname(File.expand_path(__FILE__)))
file = File.open('old.xml')
doc = REXML::Document.new(file)
File.write('new01.xml', doc)


 というソースを書いて実行した結果、こういう形で出力されました。
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE urlset><urlset xmlns='http://www.sitemaps.org/schemas/sitemap/0.9'>
<url><loc>https://freebsd.sing.ne.jp/</loc>				<lastmod>2018-10-10</lastmod><changefreq>monthly</changefreq><priority>0.3</priority></url>		<!-- トップ 	-->
<url><loc>https://freebsd.sing.ne.jp/00/</loc>			<lastmod>2018-10-30</lastmod><changefreq>monthly</changefreq><priority>0.1</priority></url>		<!-- 共通		-->

<url><loc>https://freebsd.sing.ne.jp/01/08.04/</loc>	<lastmod>2015-06-30</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>		<!-- 8.4 RELEASE -->
<url><loc>https://freebsd.sing.ne.jp/01/09.03</loc>		<lastmod>2016-12-31</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>		<!-- 9.3 RELEASE -->
<url><loc>https://freebsd.sing.ne.jp/01/10.02/</loc>	<lastmod>2016-12-31</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>		<!-- 10.2 RELEASE -->
<url><loc>https://freebsd.sing.ne.jp/01/11.00/</loc>	<lastmod>2017-11-30</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>		<!-- 11.0 RELEASE -->
<url><loc>https://freebsd.sing.ne.jp/01/10.03/</loc>	<lastmod>2018-04-30</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>		<!-- 10.3 RELEASE -->
 改行コードが変わったり、タブが空白になったり、ダブルコーテーションで囲んでいた文字列がシングルコーテーションで囲まれたりしましたが、意味合い的にはまったく同じものが出力されることが確認できました。  しめしめ。  最終目的で、ある個所を編集するのを自動でやるのはできそうです。

3. 要素を取得する

 要素を取得するには elements というメソッドを使えばいいらしいので
require('pry')
require('rexml/document')

Dir.chdir(File.dirname(File.expand_path(__FILE__)))
file = File.open('old.xml')
doc = REXML::Document.new(file)

doc.elements['urlset/url'].each do | url |
  puts("url=[#{url}]")
end


 というソースを書いて実行したら、こんなんなっちゃいまして。
url=[<loc>https://freebsd.sing.ne.jp/</loc>]
url=[				]
url=[<lastmod>2018-10-10</lastmod>]
url=[<changefreq>monthly</changefreq>]
url=[<priority>0.3</priority>]
 each で全要素を取得する予定だったのですが、これはどうも使い方が悪かったようで。  [''] で指定したものは、先頭の要素しか取り出せないようです。  「Rubyでxmlファイル読み込みを行う方法:rexml | UX MILK」を読んで、使い方が分かりました。
require('pry')
require('rexml/document')

Dir.chdir(File.dirname(File.expand_path(__FILE__)))
file = File.open('old.xml')
doc = REXML::Document.new(file)

doc.elements.each('urlset/url') do | url |
  puts("url=[#{url}]")
end


 というソースを書いて実行した結果、以下の結果が得られました(途中までしか出力していません)。
url=[<url><loc>https://freebsd.sing.ne.jp/</loc>				<lastmod>2018-10-10</lastmod><changefreq>monthly</changefreq><priority>0.3</priority></url>]
url=[<url><loc>https://freebsd.sing.ne.jp/00/</loc>			<lastmod>2018-10-30</lastmod><changefreq>monthly</changefreq><priority>0.1</priority></url>]
url=[<url><loc>https://freebsd.sing.ne.jp/01/08.04/</loc>	<lastmod>2015-06-30</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>]
url=[<url><loc>https://freebsd.sing.ne.jp/01/09.03</loc>		<lastmod>2016-12-31</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>]
url=[<url><loc>https://freebsd.sing.ne.jp/01/10.02/</loc>	<lastmod>2016-12-31</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>]
url=[<url><loc>https://freebsd.sing.ne.jp/01/11.00/</loc>	<lastmod>2017-11-30</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>]
url=[<url><loc>https://freebsd.sing.ne.jp/01/10.03/</loc>	<lastmod>2018-04-30</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>]
url=[<url><loc>https://freebsd.sing.ne.jp/10.04/</loc>		<lastmod>2018-01-22</lastmod><changefreq>yearly</changefreq><priority>0.2</priority></url>]
url=[<url><loc>https://freebsd.sing.ne.jp/11.01/</loc>		<lastmod>2018-07-18</lastmod><changefreq>monthly</changefreq><priority>0.3</priority></url>]
url=[<url><loc>https://freebsd.sing.ne.jp/11.02/</loc>		<lastmod>2018-10-15</lastmod><changefreq>monthly</changefreq><priority>0.4</priority></url>]
 要素内の要素を取り出すのはこの応用になるようで
require('pry')
require('rexml/document')

Dir.chdir(File.dirname(File.expand_path(__FILE__)))
file = File.open('old.xml')
doc = REXML::Document.new(file)

doc.elements.each('urlset/url') do | url |
  puts("loc=[#{url.elements['loc']}] lastmod=[#{url.elements['lastmod']}] lastmod.text=[#{url.elements['lastmod'].text}]")
end


 というソースを書いて実行した結果、以下の結果が得られました(途中までしか出力していません)。
loc=[<loc>https://freebsd.sing.ne.jp/</loc>] lastmod=[<lastmod>2018-10-10</lastmod>] lastmod.text=[2018-10-10]
loc=[<loc>https://freebsd.sing.ne.jp/00/</loc>] lastmod=[<lastmod>2018-10-30</lastmod>] lastmod.text=[2018-10-30]
loc=[<loc>https://freebsd.sing.ne.jp/01/08.04/</loc>] lastmod=[<lastmod>2015-06-30</lastmod>] lastmod.text=[2015-06-30]
loc=[<loc>https://freebsd.sing.ne.jp/01/09.03</loc>] lastmod=[<lastmod>2016-12-31</lastmod>] lastmod.text=[2016-12-31]
loc=[<loc>https://freebsd.sing.ne.jp/01/10.02/</loc>] lastmod=[<lastmod>2016-12-31</lastmod>] lastmod.text=[2016-12-31]
loc=[<loc>https://freebsd.sing.ne.jp/01/11.00/</loc>] lastmod=[<lastmod>2017-11-30</lastmod>] lastmod.text=[2017-11-30]
loc=[<loc>https://freebsd.sing.ne.jp/01/10.03/</loc>] lastmod=[<lastmod>2018-04-30</lastmod>] lastmod.text=[2018-04-30]
loc=[<loc>https://freebsd.sing.ne.jp/10.04/</loc>] lastmod=[<lastmod>2018-01-22</lastmod>] lastmod.text=[2018-01-22]
loc=[<loc>https://freebsd.sing.ne.jp/11.01/</loc>] lastmod=[<lastmod>2018-07-18</lastmod>] lastmod.text=[2018-07-18]
loc=[<loc>https://freebsd.sing.ne.jp/11.02/</loc>] lastmod=[<lastmod>2018-10-15</lastmod>] lastmod.text=[2018-10-15]

4. 要素を更新する

 前項までの内容を踏まえて。  要素を更新するには elements.text を書き変えればいいらしい。
require('pry')
require('rexml/document')

Dir.chdir(File.dirname(File.expand_path(__FILE__)))
file = File.open('old.xml')
doc = REXML::Document.new(file)

doc.elements.each('urlset/url') do | url |
  if (url.elements['loc'].text == 'https://freebsd.sing.ne.jp/01/10.02/')
    url.elements['lastmod'].text = '2018-11-13'
  end
end

File.write('new02.xml', doc)

 というソースを書いて、最初に出力した .xml ファイルとの diff をとると
8c8
< <url><loc>https://freebsd.sing.ne.jp/01/10.02/</loc>	<lastmod>2016-12-31</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>		<!-- 10.2 RELEASE -->
---
> <url><loc>https://freebsd.sing.ne.jp/01/10.02/</loc>	<lastmod>2018-11-13</lastmod><changefreq>never</changefreq><priority>0.0</priority></url>		<!-- 10.2 RELEASE -->
 と思い通りの結果が得られました。