スポンサーリンク

Rails と Excel VBA で,XMLファイルを読み書きしよう (MSXMLとREXMLの使い方)


Webアプリ(Ruby on Rails)とデスクトップ(Excel VBA)の間で,XMLをやり取りするサンプルコード。

  1. VBAでXMLを読み込み(そしてシート上に表示する),
  2. VBAでXMLを書き出し(シート上の情報をもとにファイルを吐きだす),
  3. Ruby on RailsからXMLを読み込み(アップロードしてパース),
  4. Ruby on RailsからXMLを書き出す(ダウンロードさせる)。

という,双方向の流れを作る。



XMLを扱うために,それぞれライブラリとして

  • VBA側 : MSXML
  • ruby側 : REXML

を利用。



想定XML:

<hoge>
	<fuga id="1">a</fuga>
	<fuga id="2">b</fuga>
</hoge>

(1)VBAでXML読み込み(MSXML)

ツール>参照設定
Microsoft XML v5.0
にチェック。

' 指定されたパスのXMLを読み込んでシートに反映
Sub load_xml( input_path )
	Set xml = New MSXML2.DOMDocument

	' 読み込み
	xml.Load input_path
	Set elem_hoge = xml.getElementsByTagName("hoge").Item(0)

	' 子要素をリストアップ
	i = 1
	For Each elem_fuga In elem_hoge.getElementsByTagName("fuga")
		Cells(i, 1).Value = elem_fuga.getAttribute("id")
		Cells(i, 2).Value = elem_fuga.Text
		
		i = i + 1
	Next elem_fuga
	
	MsgBox "読み込みました。"
End Sub

読み込み用のコードに出現するキーワード

  • Load
  • getElementsByTagName
  • getAttribute

(2)VBAでXML書き込み(MSXML)

' 指定されたパスにXML書き出し
Sub save_xml( output_path )
	Set xml = New MSXML2.DOMDocument

	' データ作成
	xml.appendChild xml.createProcessingInstruction("xml", "version='1.0' encoding='SHIFT-JIS'")
	Set elem_hoge = xml.appendChild(xml.createElement("hoge"))

	' 子要素を追加
	For i = 1 To 2
		Set elem_fuga = xml.createElement("fuga")
		elem_fuga.setAttribute "id", Cells(i, 1).Value
		elem_fuga.Text = Cells(i, 2).Value
	Next i
	
	' 保存
	xml.Save output_path
	
	MsgBox "書き出しました。"
End Sub

書き出し用のコードに出現するキーワード

  • createElement
  • createProcessingInstruction
  • appendChild
  • setAttribute
  • Save

(3)Ruby on RailsでXML読み込み(REXML)

アップロードしたXMLの内容を読み取ってDB登録するような状況を想定。


コントローラ側

  # アップロードされたXMLを解析するアクション
  def upload_xml
    # アップロード時のnameを指定
    xml_txt = params["xml_file"].read
  
    # 解析
    モデル名.parse_xml( xml_txt )
  
  end


モデル側

  require "rexml/document"


  # XMLを読み込みます。
  def self.parse_xml( xml_txt )
    # パース
    xml = REXML::Document.new xml_txt
    
    # 子要素を解析
    elem_hoge = xml.elements["hoge"]
    elem_hoge.each{|elem_fuga|
      fuga_id   = elem_fuga.attributes["id"]
      fuga_text = elem_fuga.get_text.to_s
      
      # 解析データを使った処理(DB登録とか)
      # 〜
    }
    
    nil
  end

キーワード:

  • elements
  • each
  • attributes
  • get_text


ビュー側では,formのenctypeを"multipart/form-data"にするのを忘れずに。


なお,注意点として,get_textは文字列ではなく「テキストノード」を返す。

したがって,get_text.length とかやってしまうとNoMethodErrorを起こすので用心の事。

(4)Ruby on RailsでXML書き出し(REXML)

DBの内容を読み取ってXML形式でダウンロードするような状況を想定。


コントローラ側

  # XMLをダウンロードするアクション
  def download_xml
    xml_txt = モデル名.get_xml
    
    # ダウンロードさせる
    send_data( xml_txt, :type => "text/xml; charset=shift_jis; ", :filename => "boo.xml" )
    
    # 画面に表示したい場合
    #render( :text => xml_txt, :layout => false )
  
  end


モデル側

  require "rexml/document"
  require "nkf"


  # XMLを書き出します。
  def self.get_xml

    # データ生成
    xml = REXML::Document.new
    xml << REXML::XMLDecl.new( "1.0", "Shift-JIS" )
    
    # 子要素を追加
    elem_hoge = xml.add_element( "hoge" )
    elem_fuga = elem_hoge.add_element( "fuga" )
    elem_fuga.add_attributes({
      "id" => 1
    })
    elem_fuga.add_text "a"
    
    # 文字列にダンプ
    xml_txt_utf8 = ""
    xml.write( xml_txt_utf8 ) # 出力先ストリームとして文字列変数を指定
    
    # 文字コードを変換(VBAに読ませるために)
    xml_txt_sjis = NKF.nkf( "-U -s -Lw", xml_txt_utf8 )
    
    return xml_txt_sjis
  end

キーワード:

  • add_element
  • add_attributes
  • add_text
  • write


注意点:

  • xml.writeを呼び出す際に,文字コードに注意すること。XML内の冒頭の宣言で「UTF-8」としているのに,XMLの内容がEUCで書かれていたりすると,このメソッドを呼んだ時点で例外が発生してしまう。


補足

参考リンク

VBAでxmlの情報を読み込む(MSXML)
http://pugiemonn.blog6.fc2.com/blog-e...

RubyでXML操作(REXML)
http://www.nslabs.jp/ruby-rexml.rhtml



関連エントリ:

Railsで,簡単にメッセージ管理する方法 (メッセージ定義書からメッセージ処理クラスを自動生成するVBAマクロ)
http://language-and-engineering.hatenablog.jp/entry/20090704/p1


Ruby on Railsのマイグレーションで,テストデータやサンプルデータをうまく管理する方法
http://language-and-engineering.hatenablog.jp/entry/20091211/p1


Ruby on Railsのバージョン間での違いのまとめ 一覧表(1系・2系・3系の差異と歴史)
http://language-and-engineering.hatenablog.jp/entry/20110913/p1