C-6 XML による名簿検索プログラム

XML を使う場合、「A-2 Windows Media Player」のときのように、事前の準備が必要です。ただし、XML はコントロール (実行中も目に見えるコンポーネント) ではなく非ヴィジュアルコンポーネント (TimerSaveDialog のように実行中は見えないコンポーネント) なので、「コンポーネント」→「ActiveX コントロールの取り込み」ではなく「プロジェクト」→「タイプライブラリの取り込み」を使います。

【練習問題】

XML を使う準備をしましょう。まずは、「プロジェクト」→「タイプライブラリの取り込み」を選びます。

すると、タイプライブラリというものの選択画面になります。

次に、一番上の一覧から「Microsoft XML」を選ぶと、その下の「クラス名」のところに「TDOMDocument」「TDOMDocument26」「TDOMDocument30」という名前が現れます。(Microsoft 社製の) XML コンポーネントはこの名前で Delphi に登録されます。

注意 上の例では「Microsoft XML」のバージョンが4.0である場合を想定していますが、パソコン環境によってはバージョンが2.0の場合があります。そのときの「クラス名」はそれぞれ「TDOMDocument」「ThreadedDocument」「TXMLHTTPRequest」となり上と異なりますが、以下の動作に支障はありません。

コラム Microsoft XML がいくつもある場合 / 1つもない場合

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 というユニットをプロジェクトに追加して下さい。

usexml ユニットをダウンロード(D)

注意 先にインストールした「Microsoft XML」のバージョンが 2.0 だった人は、usexml ユニット内の uses 節における MSXML2_TLBMSXML_TLB にしてください ("2"を取る)。

コラム usexml ユニットの中身は

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;

次は、「開く」ボタンのイベントハンドラを書き換えます。「開く」ボタンを押したときの動作は、

  1. データ全体に名前を付ける
  2. データを読み込む
  3. データの根に名前を付ける

という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 です。

コラム as とは何か

プログラム中に出てくる

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 を使ってもよいかどうかが判断できるようになります。

ここまでのプログラムをダウンロード(D)