【IRIS/Cache】XMLファイルを読み込む

今回はXMLファイルの読み込みについて解説いたします。

XMLファイルを取り込みたい方は、この記事を一読していただけると幸いです。

※この記事は下記の方向けになります。
  • XMLファイルを読み込んだ事がないユーザ
  • 独自のXMLパーサーがない

はじめに

XMLファイルの読み込みは、クラス「%XML.TextReader.cls」の関数「ParseFile」を使用します。
CSVファイルの読み込みとは異なり、かなり癖が強いです。

サンプルとして下記XMLファイルを用意しました。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<XmlMsg>
	<MessageList>
		<Person no="10000000" birthday="1980-09-02">藤原道長</Person>
		<Person no="10000001" birthday="2021-09-02">紫式部</Person>
	</MessageList>
</XmlMsg>

読み込み

CSVファイルを読み込む処理が、下記サンプルです。

関数「ParseFile」に対しXMLファイルの渡し、オブジェクトを受け取ります。
後は、取得したオブジェクトから「エレメント情報」を取得します。

ClassMethod main(filePath As %String) As %Status
{
	s sts = $$$OK
	try{
		$$$ThrowOnError(##class(%XML.TextReader).ParseFile(filePath, .obj))
		while (obj.Read()){
			w !,obj.NodeType,",",obj.Name,",",obj.Value
		}
	}catch e {
		s sts = e.AsStatus()
	}
	q sts
}

では、読み込んでみましょう。
下記コマンドをターミナルから実行します。

d ##class(developer.xml.Sample).main("D:\Temp\xml\Sample.xml")

実行結果です。

・・・癖が強い。
各エレメント毎に取得しているため、開始エレメント「element」と終了エレメント「endelement」が別々で取得しています。

また、エレメントのデータ部「藤原道長」「紫式部」は、エレメント「chars」で取得できています。
おまけに、「no」「birthday」が取得できていません。

う~ん
javascriptで取得するHTML DOMのような仕様にしてもらえませんかね・・・
あまりにも使い勝手が悪すぎる!

一部修正をする

あくまで一例ではありますが、狙ったデータを取得するため一部修正しています。

先ずは、エレメント「Person」よりAttribute値no」「birthday」の取得を行っています。
後はエレメント「chars」より、人物名を取得しています。

ClassMethod main(filePath As %String) As %Status
{
	s sts = $$$OK
	try{
		s text = ""
		$$$ThrowOnError(##class(%XML.TextReader).ParseFile(filePath, .obj))
		while (obj.Read()){
			
			i (obj.NodeType = "element")&&(text="")&&(obj.Name = "Person"){
				s:(obj.MoveToAttributeName("no")) text = obj.Value
				s:(obj.MoveToAttributeName("birthday")) text = text_","_obj.Value
			}
			i (text'="")&&(obj.NodeType = "chars"){
				s text = text_","_obj.Value				
				w !,text
			}
			i (obj.NodeType = "endelement")&&(text'="")&&(obj.Name = "Person"){
				s text = ""
			}
		}
	}catch e {
		s sts = e.AsStatus()
	}
	q sts
}

では、読み込んで実行結果を確認しましょう。

一先ず、必要なデータは取得できたようです。

Attribute値に関しては、決め打ちで取得していましたが、汎用的に記述するなら下記方法もあります。

ClassMethod main(filePath As %String) As %Status
{
	s sts = $$$OK
	try{
		s text = ""
		$$$ThrowOnError(##class(%XML.TextReader).ParseFile(filePath, .obj))
		while (obj.Read()){
			
			i (obj.NodeType = "element")&&(text="")&&(obj.Name = "Person"){
				k attrDt
				f pos = 1:1:obj.AttributeCount {
         			d obj.MoveToAttributeIndex(pos)
         			s attrDt(obj.LocalName)=obj.Value
     			}
     			s text=1
			}
			i (text)&&(obj.NodeType = "chars"){
				s text = attrDt("no")_","_obj.Value_","_attrDt("birthday")	
				w !,text
			}
			i (obj.NodeType = "endelement")&&(text'="")&&(obj.Name = "Person"){
				s text = ""
			}
		}
	}catch e {
		s sts = e.AsStatus()
	}
	q sts
}

グローバルで取得する

データ取得用のPGを記述するのが手間だと感じたら、一旦グローバルに展開するのも一つの手になります。

最後の引数にグローバル名を文字列で渡すと、その中にXMLデータを詰めて返してきます。
下記がサンプルになります。
 ※グローバル名の初期値は「^||IRIS.Temp」です。

ClassMethod main(filePath As %String) As %Status
{
	s sts = $$$OK
	try{
		s text = ""
		$$$ThrowOnError(##class(%XML.TextReader).ParseFile(filePath, .obj,,,,,,$na(^||sample)))
		zw ^||sample
	}catch e {
		s sts = e.AsStatus()
	}
	q sts
}

では、実行してみます。

SAMPLE>d ##class(developer.xml.Sample).main("D:\Temp\xml\Sample.xml")
^||sample=3
^||sample(3,1)=$lb("element","","XmlMsg","XmlMsg",0,1)
^||sample(3,2)=$lb("element","","MessageList","MessageList",0,2)
^||sample(3,3)=$lb("element","","Person","Person",2,3)
^||sample(3,3,1)=$lb("","no","no","CDATA","10000000")
^||sample(3,3,2)=$lb("","birthday","birthday","CDATA","1980-09-02")
^||sample(3,4)=$lb("chars","藤原道長","")
^||sample(3,5)=$lb("endelement","","Person","Person")
^||sample(3,6)=$lb("element","","Person","Person",2,3)
^||sample(3,6,1)=$lb("","no","no","CDATA","10000001")
^||sample(3,6,2)=$lb("","birthday","birthday","CDATA","2021-09-02")
^||sample(3,7)=$lb("chars","紫式部","")
^||sample(3,8)=$lb("endelement","","Person","Person")
^||sample(3,9)=$lb("endelement","","MessageList","MessageList")
^||sample(3,10)=$lb("endelement","","XmlMsg","XmlMsg")

グローバルに展開されれば、何となく取りやすくなった感じがする・・・ような?
ちょっとだけですかね(笑
Attribute値に関しては、断然分かりやすくなった感じがします。

XMLファイル読み込みの手段として、検討してみてください。

おわりに

XMLファイルは、CSVと異なり力技で解決するのが難しいファイルになります。
そのためパーサーに頼るのが一番ですが、IRIS/Cacheのパーサーはかなり癖が強いですよね。

この記事を読んで、XMLファイルの読み込みがスムーズにできる事を願っています。