【IRIS/Cache】XMLファイルを作成する

XMLファイルの出力方法をご紹介いたします。
この記事では、下記の方向けになります。

※この記事は下記の方向けになります。
  • IRIS/CacheでXMLファイルを出力した事がない
  • XMLファイルを出力したい
  • テーブルのレコードからXMLファイルを出力したい

はじめに

今回は、XMLファイルを出力する方法をご紹介いたします。

IRIS/CacheでXMLファイルを出力するには、主に「%XML.Writer.cls」を使用します。
データクラスから直接XMLファイルを作成する場合は、スーパークラス(Extends)に「%XML.Adaptor」を設定します。

両者とも、特に小難しい設定はありません。
サンプルを交えながら解説したいと思います。

XMLファイルの読み込みに関しては、下記記事を参照してください。

XMLファイルの出力

XMLファイルを出力するには、クラス「%XML.Writer.cls」を使用します。

サンプルは下記になります。

ClassMethod write(filePath As %String, gblNm As %String, indentChar As %String = " ", nmSp As %String = "")
{
	try {
		s xml=##class(%XML.Writer).%New()
		, xml.Charset = "UTF-8"
		, xml.Indent = 1
		, xml.IndentChars = indentChar
		s:(nmSp'="") xml.DefaultNamespace = nmSp // ネームスペースの設定
		d xml.OutputToFile(filePath) // ファイル出力

		// rootエレメント
		s $lg(keyNm) = @gblNm // ルートエレメント名取得
		$$$ThrowOnError( xml.RootElement(keyNm) )
		
		// 子エレメント
		s keyNum="" 
		f { s keyNum = $o(@gblNm@(keyNum)) q:keyNum="" 
			d ..roopElm(xml, $na(@gblNm@(keyNum)))
		}
		
		$$$ThrowOnError( xml.EndRootElement() ) // rootエレメント閉じ
	}catch e{
		w !,$system.Status.DisplayError(e.AsStatus())
	}
}

/// 回帰処理
ClassMethod roopElm(xml As %XML.Writer, gblNm As %String)
{
	// エレメント情報
	s $lg(keyNm, keyVal, AttrDt, nmSp, Comment) = @gblNm
	
	i (keyNm '= ""){
		d xml.Element(keyNm, nmSp)
		
		// アトリビュート設定
		i (AttrDt '= ""){
			s ptr = 0
			while( $listNext(AttrDt, ptr, attr)){
				s $lg(attrKey, attrVal) = attr
				d xml.WriteAttribute(attrKey, attrVal)
			}
		}
		
		// 値セット
		d:(keyVal'="") xml.WriteChars(keyVal)
		// コメント
		d:(Comment'="") xml.WriteComment(Comment)
		

		// 下位ノードの描画
		i ($d(@gblNm)=11){
			s keyNum = ""
			f { s keyNum = $o(@gblNm@(keyNum),1,data) q:keyNum=""
				d ..roopElm(xml, $na(@gblNm@(keyNum)))
			}		
		}elseif($d(@gblNm)=10){
			// エラーパターン
			throw ##class(%Exception.General).%New("配列ミス","5001","", "gblNm:"_gblNm)
		}
		
		d xml.EndElement()
	}elseif(Comment '= "") {
		// コメントのみ出力
		d xml.WriteComment(Comment)
	}
}

【コマンド実行】

グローバルの設定は下記になります。
^||data = $lb([ルートエレメント名])
^||data([1階層],[2階層],…)=$lb([エレメント名],[値],$lb(アトリビュート),[ネームスペース],[コメント])

上記を元に、記事「XMLファイルを読み込む」で使用したXMLファイルを作成(+α)したいと思います。

s fileName = "D:\Temp\xml\CreateXML.xml"
k ^||data
s ^||data = $lb("PersonData")
s ^||data(1) = $lb("PersonList")
s ^||data(1, 1) = $lb(, , , ,"藤氏長者")
s ^||data(1, 2) = $lb("Person", "藤原道長", $lb($lb("no", "10000000"),$lb("birthday","1980-09-02")),"testNm")
s ^||data(1, 3) = $lb("Person", "紫式部", $lb($lb("no", "10000001"),$lb("birthday","2021-09-02")),, "源氏物語の作者")

d ##class(developer.xml.Sample).write(fileName, $na(^||data))

実行すると下記XMLファイルが出力されます。

<?xml version="1.0" encoding="UTF8"?>
<PersonData>
 <PersonList>
  <!--藤氏長者-->
  <Person xmlns="testNm" no="10000000" birthday="1980-09-02">藤原道長</Person>
  <Person no="10000001" birthday="2021-09-02">紫式部<!--源氏物語の作者--></Person>
 </PersonList>
</PersonData>

解説

先ずは、「%XML.Writer.cls」をインスタンス化するところから始めます。

s xml=##class(%XML.Writer).%New()
, xml.Charset = "UTF-8"
, xml.Indent = 1
, xml.IndentChars = indentChar
s:(nmSp'="") xml.DefaultNamespace = nmSp
d xml.OutputToFile(filePath)
プロパティ説明
Charset文字コードを指定します(デフォルト値:UTF-8)
他:SJIS, Shift-JIS等
Indent0=1行で出力される(デフォルト値:1)
取り込み用のファイルでなければ、1の方が可読性が高いです
IndentCharsインデント用の文字(デフォルト値:” ” ※半角スペース)
他:$c(9) ※タブ 等々
DefaultNamespaceネームスペースの指定
ルートタグに「xmlns=”[ネームスペース名]”」が表示される
OutputToFileファイル出力を行う
 ※エレメント関連を描画する関数を実行するより前に記述する必要あり

他に、OutputToStream, OutputToString, OutputToDeviceがあり、出力先によって適宜変更する
プロパティ説明

他にも設定できるプロパティがあります。
詳細は公式ドキュメントを確認してください。

ルートタグの描画

ルートエレメントの設定を行います。
今回の関数では、「^||data = $lb(“ルートエレメント名”)」の設定になります。

// rootエレメント
s data = @gblNm
s keyNm = $li(data,1)
$$$ThrowOnError( xml.RootElement(keyNm) )

~~ 略 (子エレメントの描画処理を記述)~~

$$$ThrowOnError( xml.EndRootElement() ) // rootエレメント閉じ

RootElement()~EndRootElement()間に、子エレメントを書き込む処理を記述します。

各エレメントの描画

サンプルでは、回帰処理を実行しつつ子エレメントを描画していきます。
主な関数は「roopElm」です。

エレメントの描画と閉じの関数です。

d xml.Element([エレメント名], [ネームスペース名])

~~ 略 (値とアトリビュートの設定と子エレメントの描画を行う)~~

d xml.EndElement() // エレメント閉じ

値の描画を行います。※Element()~EndElement()の間に記述します

d xml.WriteChars([値])

アトリビュートの描画を行います。※Element()~EndElement()の間に記述します

d xml.WriteAttribute([属性名], [値])

コメントの描画を行います。
基本的にどこでも記述可能です。

d xml.WriteComment([コメント])

XSLTスタイルシートタグの追加方法

割と強引にも思えますが、下記関数でXSLTスタイルシートの宣言が可能です。

d xml.WriteProcessingInstruction("xml-stylesheet", "type=""text/xsl"" href=""testxsl.xsl""")

XMLファイルを出力すると、最初の2行目が追加されます。

<?xml version="1.0" encoding="Shift-JIS"?>
<?xml-stylesheet type="text/xsl" href="testxsl.xsl"?>
<XmlMsg>

DOCTYPE宣言の方法

XMLと構造を定義したDTDとを結びつける、DOCTYPEが出力できます。

d xml.WriteDocType("俳優情報",,," <!ELEMENT 俳優リスト (俳優)>"_$c(13,10)_" <!ELEMENT 俳優 (#PCDATA)>")

XMLファイルを出力すると、DOCTYPE宣言が出力されます。

<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE 俳優情報 [
 <!ELEMENT 俳優リスト (俳優)>
 <!ELEMENT 俳優 (#PCDATA)>
]>

XML宣言行に「standalone=”no”」を出力する

時にはXML宣言の行に「standalone=”[no or yes]”」を表示する時があります。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

ただし、現在「%XML.Writer.cls」では、このXML宣言を変更する手段がありません。
そのため、XML宣言を非表示にする設定を行った後、自前のXML宣言を描画します。

s xml.NoXMLDeclaration=1 // XML宣言を出力しない
d xml.WriteProcessingInstruction("xml", "version=""1.0"" encoding=""UTF-8"" standalone=""no""")

XMLファイルを出力すると、「standalone=”no”」が出力されます。

CDATAの描画

下記コマンドでCDATAセクションの出力が行えます。

d xml.Element([エレメント名]) // 任意のエレメント名
, xml.WriteCData("0 > 1")
, xml.EndElement() // エレメント閉じ

XMLファイルを出力すると、CDATAセクションが出力されます。

<[エレメント名]><![CDATA[0 > 1]]></[エレメント名]>

テーブルのレコードからXMLファイルを出力したい

テーブルのプロパティ名をエレメントとして、XMLファイルを出力する事が可能です。

前準備

テーブルにクラス「%XML.Adapter.cls」を継承させます。

Class developer.csv.Patient Extends (%Persistent, %XML.Adaptor)

XMLファイルの出力

%XML.Writer.clsの設定は変わりません。
関数「xml.Object()」を利用して、レコード情報からXMLデータを出力します。

サンプルは下記になります。

ClassMethod writeOBJ(filePath As %String, tagNm As %String, obj As %Persistent, indentChar As %String = " ", nmSp As %String = "")
{
	try {
		s xml=##class(%XML.Writer).%New()
		, xml.Charset = "UTF-8"
		, xml.Indent = 1
		, xml.IndentChars = indentChar
		s:(nmSp'="") xml.DefaultNamespace = nmSp
		d xml.OutputToFile(filePath)

	
		// rootエレメント
		$$$ThrowOnError( xml.RootElement(tagNm) )
		
		$$$ThrowOnError( xml.Object(obj, obj.%ClassName()) )
		
		$$$ThrowOnError( xml.EndRootElement() ) // rootエレメント閉じ
	}catch e{
		w !,$system.Status.DisplayError(e.AsStatus())
	}
}

【コマンド実行】

s obj = ##class(developer.csv.Patient).%OpenId(1)
s fileName = “D:\Temp\xml\CreateOBJXML.xml”

d ##class(developer.xml.Sample).writeOBJ(fileName, “Object”, obj)

コマンドを実行すると、下記XMLファイルが作成されます。

<?xml version="1.0" encoding="UTF-8"?>
<Object>
 <Patient>
  <xActive>true</xActive>
  <zUpdateCount>1</zUpdateCount>
  <dele>false</dele>
  <storeCd>1173</storeCd>
  <patientId>1173412876</patientId>
  <漢字氏名>田中 一郎</漢字氏名>
  <カナ氏名>タナカ イチロウ</カナ氏名>
  <ローマ字氏名>Tanaka Ichiro</ローマ字氏名>
  <性別>男</性別>
  <生年月日>1995-08-02</生年月日>
  <死亡日時>1900-01-01T00:00:00Z</死亡日時>
  <コメント>骨折治療</コメント>
  <新患登録日>1916-06-29</新患登録日>
  <ABO血液型>AB</ABO血液型>
  <RH血液型>+</RH血液型>
  <未使用区分>true</未使用区分>
 </Patient>
</Object>

プロパティ名がエレメント名となり、値が出力されています。
かなり簡単に出力できるので、利用できるタイミングでは是非お勧めしたい方法になります。

おわりに

いかがだったでしょうか。

XMLファイルの出力はCSVファイルと異なり、構造的でかなり小難しく感じますが、クラス「%XML.Writer.cls」に沿って設定していく事で、かなり簡単に出力できると思います。

ここに記載しなかった細かい設定に関しては、是非ドキュメントをご確認ください。