XML を使う場合、「A-2 Windows Media Player」のときのように、事前の準備が必要です。ただし、XML はコントロール (実行中も目に見えるコンポーネント) ではなく非ヴィジュアルコンポーネント (Timer やSaveDialog のように実行中は見えないコンポーネント) なので、「コンポーネント」→「ActiveX コントロールの取り込み」ではなく「プロジェクト」→「タイプライブラリの取り込み」を使います。
XML を使う準備をしましょう。まずは、「プロジェクト」→「タイプライブラリの取り込み」を選びます。
すると、タイプライブラリというものの選択画面になります。
次に、一番上の一覧から「Microsoft XML」を選ぶと、その下の「クラス名」のところに「TDOMDocument」「TDOMDocument26」「TDOMDocument30」という名前が現れます。(Microsoft 社製の) XML コンポーネントはこの名前で Delphi に登録されます。
注意 上の例では「Microsoft XML」のバージョンが4.0である場合を想定していますが、パソコン環境によってはバージョンが2.0の場合があります。そのときの「クラス名」はそれぞれ「TDOMDocument」「ThreadedDocument」「TXMLHTTPRequest」となり上と異なりますが、以下の動作に支障はありません。
Microsoft XML がいくつもある場合は、最も新しいバージョン (上の例では v4.0) を選んでください。
Microsoft XML が1つもない場合は、Microsoft XML ダウンロードページ http://msdn.microsoft.com/downloads/default.asp?URL=/code/topic.asp?URL=/msdn-files/028/000/072/topic.xml からダウンロードしてインストールしてください。
そのまま「インストール」ボタンを押してもいいのですが、Microsoft XML のコンポーネントは数が多いので、「XML」という新しいパレットページを作り、そこにインストールすることにします。「パレット ページ名」の欄を「XML」に変更してから「インストール」ボタンを押して下さい。
ここで、XML に関するユニットファイルの保存先をきいてきますが、A-3 節の「ActiveX コントロールの取り込み」の時と同様、各個人の保存用フォルダに適当なフォルダを用意し、その中に保存してください。不明な点があれば、教員あるいは指導員に尋ねてください。保存および (XML に関する) ユニットファイルのコンパイルが完了すると、XML のコンポーネントがコンポーネントパレットに現れます(取り込む XML のバージョンによっては、下の画面と少々違うかも知れませんが、気にする必要はありません)。
ここまでで、タイプライブラリの取り込みは終わり、XML が使えるようになりました。
これで、XML を使う準備が出来ました。この後は、XML を用いる形へプログラムを変更していきます。XML 文書の操作は、DOM と呼ばれる方法を使います。DOM で使う主なクラス・プロパティ・メソッドは次の通りです。
クラス | プロパティ・メソッド |
---|---|
IXMLDOMDocument XML 文書全体を表すクラスです。usexml ユニットを使うと、XMLDocument というこのクラスのオブジェクトが自動的に作られます。 |
load(<ファイル名>) (メソッド) XML 文書ファイルを読み込みます。 |
save(<ファイル名>) (メソッド) XML 文書ファイルを保存します。 |
|
Get_documentElement (関数) XML 文書ファイルから根要素を取り出します。返り値は IXMLDOMElement 型です。 |
|
CoDOMDocument
上欄の IXMLDOMDocument クラスのオブジェクト (要するに XML 文書) を生成するために用意されているクラスです。 |
Create (メソッド) XML 文書ファイルを生成します。 |
IXMLDOMElement XML 文書の中の要素・節 (ノード) を表すクラスです。 |
getElementsByTagName(<要素名>) (関数) その要素にぶら下がっている子要素から、指定された名前の要素を取り出します。返り値は、要素1個とは限らず複数見つかる可能性があるため、IXMLDOMNodeList 型となります。 |
text (プロパティ) その要素の中身を示します。 |
|
IXMLDOMNodeList XML 文書の中の要素・節 (ノード) の集合を表すクラスです。 |
length (プロパティ) その集合の中にいくつ要素・節 (ノード) が含まれているか、を示します。 |
item[N] (プロパティと考えてよい) その集合の中の N 番目の要素を示します。 |
何よりも先に、「このプログラムで XML を使う」という宣言が必要です。プログラムの冒頭部、interface のすぐ下の uses 節に次のように書き加えましょう。
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, MSXML2_TLB;
注意 先にインストールした「Microsoft XML」のバージョンが 2.0 だった人は、上の MSXML2_TLBを MSXML_TLBにしてください ("2"を取る)。
続いて「新規」ボタンです。XML を新たに扱う処理は少々複雑なので、その処理 (や他の複雑な処理) を手続きや関数として用意しました。まず、以下から圧縮ファイルをダウンロードし、その中にある usexml というユニットをプロジェクトに追加して下さい。
注意 先にインストールした「Microsoft XML」のバージョンが 2.0 だった人は、usexml ユニット内の uses 節における MSXML2_TLB を MSXML_TLB にしてください ("2"を取る)。
usexml ユニットの中には、次の2つの関数・手続きが入っています。
MakeRoot(<根要素の名前>)−手続き | XML 文書の根本に存在する要素を「根」と言います。この文書の場合は「メンバーデータ」です。MakeRoot は、その根を設定します。 |
AddChild(<親要素>, <新要素の名前>, [新要素の中身])−関数 | 新しい要素を作り、それを親要素の下にぶら下げます。また、AddChild の返り値は新要素の要素番号であり、これを使うと新要素の下に更に新しい要素を作ることが出来ます (C-7 で扱います)。 |
あまり複雑な処理はしていないので、興味があったら usexml ユニットの中身を覗いてみるといいでしょう。ただし、動的配列を使っているので、動的配列の知識が必要になります。動的配列については、11-7および11-8節を参照してください。
この usexml ユニットをもともとのフォームから使えるように、implementation のすぐ下に uses 節を次のように書き込みましょう。
implementation uses usexml; {$R *.DFM}
これで、複雑な処理を usexml ユニットに任せることが出来ます。「新規」ボタンのイベントハンドラを次のように書き換えてください。
// 「新規」ボタン procedure TForm1.ButtonNewClick(Sender: TObject); begin // 生データを保持する入れ物を作る MakeRoot('メンバーデータ'); // 「メンバーデータ」という名の根を作る AddChild(Root, '団体名', EditOrgName.Text); // 団体名を設定 end;
次は、「開く」ボタンのイベントハンドラを書き換えます。「開く」ボタンを押したときの動作は、
という3つから成り立ちます。
// 「開く」ボタン procedure TForm1.ButtonOpenClick(Sender: TObject); begin if (OpenDialog1.Execute = true) then begin // データそのものを XMLDocument と呼ぶ XMLDocument := CoDOMDocument.Create; XMLDocument.load(OpenDialog1.FileName); // データ読み込み Root := XMLDocument.Get_documentElement; // データの根を Root と呼ぶ end ; end;
注意
上のプログラムにおいて、XMLDocument および Root は、usexml ユニットで宣言されている (外部ユニットからも参照可能な) グローバル変数です。変数の型はそれぞれ、IXMLDOMDocument 型および IXMLDOMElement 型です。
今度は「保存」ボタンのイベントハンドラです。こちらは、XMLDocument という文書そのものに対して保存命令を出す (保存メソッドを呼ぶ) だけです。
// 「保存」ボタン procedure TForm1.ButtonSaveClick(Sender: TObject); begin if (SaveDialog1.Execute = true) then begin XMLDocument.Save(SaveDialog1.FileName); // データ保存 end ; end;
「追加」ボタンでは、「どの節 (ノード) に対してデータを追加するか」ということを指定します (この場合は Root)。
// 「追加」ボタン procedure TForm1.ButtonAddClick(Sender: TObject); begin AddChild(Root, 'メンバー', EditAdd.Text); // データ追加 EditAdd.Text := ''; // 次の入力がやりやすいように欄の中身を消去 end;
最後は「検索」ボタンのイベントハンドラです。XML を使うと、このような検索は1行で済んでしまいます。検索結果の表示処理はほぼ同じです。
// 「検索」ボタン procedure TForm1.ButtonSearchClick(Sender: TObject); var MemberList: IXMLDOMNodeList; // メンバー取り出し用リスト変数 Member: IXMLDOMElement; // メンバー取り出し用変数 i: Integer; // for 文用カウンタ begin // 「メンバー」要素を抽出、結果は複数個出てくる MemberList := Root.getElementsByTagName('メンバー'); // 検索結果表示 if (MemberList.length <> 0) then // 「メンバー」要素が1つでもあれば begin for i := 0 to MemberList.length - 1 do // 1要素ずつ begin // 取り出す Member := (MemberList.item[i] as IXMLDOMElement); if (AnsiPos(EditSearch.Text, Member.text) > 0) then // キーワードが含まれていれば begin // 表示する ShowMessage(Member.text); end ; end; end ; end;
修正し終えたら、実行してみましょう。「Rawdata : private 部で宣言されていますが、クラス内でまったく使用されていません」というエラーが出ましたね。そう、Rawdata という変数はもう不要なのです。プログラムの冒頭部から次の部分を取り除いて下さい。
type TForm1 = class(TForm) ButtonNew: TButton; EditSearch: TEdit; ButtonSearch: TButton; ButtonOpen: TButton; ButtonSave: TButton; EditAdd: TEdit; ButtonAdd: TButton; EditOrgName: TEdit; Label1: TLabel; OpenDialog1: TOpenDialog; SaveDialog1: TSaveDialog; procedure ButtonOpenClick(Sender: TObject); procedure ButtonSaveClick(Sender: TObject); procedure ButtonNewClick(Sender: TObject); procedure ButtonAddClick(Sender: TObject); procedure ButtonSearchClick(Sender: TObject); private { Private 宣言 } RawData: TStringList; // 右上のメモの代わり public { Public 宣言 } end;
これで出来上がりです。実行して色々なデータを入力し、保存した XML 文書が Internet Explorer などで正しく表示されれば OK です。
プログラム中に出てくる
Member := (MemberList.item[i] as IXMLDOMElement);
とは、「この MemberList.item[i] を IXMLDOMElement 型と見なして Member へ代入せよ」という命令です。
なぜこの as IXMLDOMElement が必要かというと、MemberList.item[i] の部分は本当は IXMLDOMNode 型という型であり、そのままでは Member へ代入できないからです。
しかし、この as はいつでも使えるわけではありません。
var a: String; b: Integer; begin a := '123'; b := a as Integer; // 普通は b := StrToInt(a); end;
などと StrToInt の代わりに as を使おうとしても、エラーになってしまいます。しかし、「どういう場面なら as が使えるのか」という話は難しくなるのでここでは省略します。将来、ポインタという概念を理解できるようになったら、as を使ってもよいかどうかが判断できるようになります。