Delphi XMLデータバインディングを使ってみる

<h1>はじめに</h1>
Delphi の XMLデータバインディング凄い便利！嬉しい！ と思ったのですが、ちょっと分からない所があったので、わかった部分を紹介しつつ、疑問点を書いてみたいと思います。

開発環境は Delphi XE3 です。

基本的に、エンバカデロの解説ページを端折って紹介しているので、詳しい情報はそちらを見て頂いた方が良いかと思います。
[XML データ バインディング ウィザードを使用する](http://docwiki.embarcadero.com/RADStudio/XE3/ja/%EF%BC%BBXML_%E3%83%87%E3%83%BC%E3%82%BF_%E3%83%90%E3%82%A4%E3%83%B3%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0_%E3%82%A6%E3%82%A3%E3%82%B6%E3%83%BC%E3%83%89%EF%BC%BD%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B)
[XML_データバインドウィザードが生成するコードの使い方](http://docwiki.embarcadero.com/RADStudio/XE3/ja/XML_%E3%83%87%E3%83%BC%E3%82%BF%E3%83%90%E3%82%A4%E3%83%B3%E3%83%89%E3%82%A6%E3%82%A3%E3%82%B6%E3%83%BC%E3%83%89%E3%81%8C%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9)

<h1>使い方</h1>

例として、学校の試験成績を記録するXMLファイルを考えてみます。

```xml
<TestResult>
	<Student>
		<Id>01234</Id>
		<Name>田中　太郎</Name>
		<Score>
			<Cource>English</Cource>
			<Date>2014/05/30</Date>
			<Value>80</Value>
		</Score>
		<Score>
			<Cource>Mathematics</Cource>
			<Date>2014/05/30</Date>
			<Value>80</Value>
		</Score>
	</Student>
</TestResult>
```

上記をXMLファイルとして保存しておきます（エンコードは UTF-8 としておきました）。

<h2>XMLデータバインディングウィザード</h2>
手始めに、新規のフォームアプリケーションを作成します。

次に、ファイル→新規作成→その他 を選択し、ダイアログからXMLデータバインディングを選択します。
![新規作成画面](https://qiita-image-store.s3.amazonaws.com/0/33684/3ca5973e-a764-615b-58f4-6c837ca3abec.png)

先ほど作成したXMLファイルを、入力し、次へを選択します。
![データバインディングウィザード１](https://qiita-image-store.s3.amazonaws.com/0/33684/bbf07172-7ceb-25e0-98b8-ba5dc91edfea.png)
</br>

ファイル内のタグの整合性に問題が無ければ、タグ要素を解析し、以下のような画面が表示されます。この画面で、タグ要素の取り込み方法を設定できます。


（下画面は、左側のツリーリストで、TestResultTypeをクリックした状態です。）
![データバインディングウィザード２](https://qiita-image-store.s3.amazonaws.com/0/33684/80c97267-0adf-a4df-c5f1-6598920a3ea9.png)

取り込んだXMLファイルには、Student要素が１つしかなかった為、Student要素の繰り返しのチェックがオフになっています。Score要素は予め繰り返し要素となっています。
![データバインディングウィザード３](https://qiita-image-store.s3.amazonaws.com/0/33684/65a65682-a80d-243f-120b-84e9d5f7f432.png)

Student要素は、繰り返し有り、と設定を変更しておきます。

テキストデータが入っているタグは、値をどの型で読み取るかを選択できます。
元ファイルに数字で入っていれば、自動でInteger型としてくれます。独自の型に変換したい場合は、一旦Stringとしておき、コード生成後に修正をする事になると思います。
![データバインディングウィザード４](https://qiita-image-store.s3.amazonaws.com/0/33684/7f99d6e6-8d02-40b7-2284-aca1d6b6f7e4.png)

要素の設定を確認後、次へをクリックすると、確認画面となります。
![データバインディングウィザード５](https://qiita-image-store.s3.amazonaws.com/0/33684/4bbb1a61-e970-55f5-0917-2cffb7a2e7ba.png)

完了をクリックすると、コードが自動生成されます！
（コード量が多いので割愛）

なお、自動生成されたコードのユニット名は、最初に取込んだXMLファイル名となります。適宜変更して保存してください。


<h2>使用してみる</h2>
![テスト用フォーム](https://qiita-image-store.s3.amazonaws.com/0/33684/8e562291-a7ec-8227-58b0-8fd59563e20f.png)

</br>

```delphi

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Sample, XMLIntf;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  WriteXml:IXMLTestResultType;
begin
  WriteXml := NewTestResult;
  WriteXml.Add;
  WriteXml.Student[0].Id := 1000;
  WriteXml.Student[0].Name := '佐藤 二郎';
  WriteXml.Student[0].Score.Add ;
  WriteXml.Student[0].Score[0].Date   := Date;
  WriteXml.Student[0].Score[0].Cource := 'English';
  WriteXml.Student[0].Score[0].Value  := 100;
  WriteXml.Student[0].Score.Add ;
  WriteXml.Student[0].Score[1].Date   := Date;
  WriteXml.Student[0].Score[1].Cource := 'Science';
  WriteXml.Student[0].Score[1].Value  := 40;

  Memo1.Lines.Assign(WriteXml.OwnerDocument.XML);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  i,j:integer;
  ReadXml:IXMLTestResultType;
  Student: IXMLStudentType;
  Score: IXMLScoreType;
begin
  if OpenDialog1.Execute then begin

    Memo1.Clear;
    ReadXml := LoadTestResult(OpenDialog1.Files[0]);

    for i:= 0 to ReadXml.Count-1 do begin
      Student := ReadXml.Student[i];
      Memo1.Lines.Add('Id:'+IntToStr(Student.Id));
      Memo1.Lines.Add('Name:'+Student.Name);
      for j := 0 to Student.Score.Count-1 do begin
        Score := Student.Score[j];
        Memo1.Lines.Add('  Score('+Score.Cource+') :'+IntToStr(Score.Value) );
      end;
    end;

  end;
end;

end.

```

<h2>実行結果</h2>
![オブジェクト作成ボタンを押した時ー](https://qiita-image-store.s3.amazonaws.com/0/33684/2080a6c2-5fe7-b42b-75b3-580e3171241f.png)
Button1Clickイベントで作成したオブジェクトがXMLテキストに変換出来ました。嬉しい！

![ファイル読み込みボタンを押した時ー](https://qiita-image-store.s3.amazonaws.com/0/33684/142c65f2-ea4b-9748-cfb2-096dde87eae3.png)
（テストとして、最初にテンプレートとして使用したXMLファイルを読み込みました）
ファイルも無事パース出来ました！嬉しい！

<h1>疑問点</h1>
Qiita用に作り直している内に起きなくなったエラーも幾つかあり・・そこはまだ切り分けが出来ていません。

<h2>for～inが使用できない？</h2>
ファイルを読み込んでいる箇所ですが、本当は

```delphi
for Student in ReadXml do 
```
と書きかったんですが、GetEnumerater が見つかりませんというコンパイルエラーが。
なにかパーサー側に修正が必要なのかも？

<h2>XMLタグの整形はできない？</h2>
オブジェクトからXMLテキストを書き出す場面ですが、

```delphi
WriteXml.OwnerDocument.Options := WriteXml.OwnerDocument.Options +[doNodeAutoIndent];
WriteXml.OwnerDocument.NodeIndentStr := '  ';
```

と設定しても、WriteXml.OwnerDocument.XML から取得できるXMLテキストには変化が無く。Delphi7 の TXMLDocumentでは出来ていたような気がしたのですが・・。
（テキストエディタで見たときに見辛いだけなので、大きな問題ではないと思いつつ）

<h1>おわりに</h1>
XMLデータバインディング便利です。抱いて！