XML Tutorial/ja
│
Deutsch (de) │
English (en) │
español (es) │
français (fr) │
magyar (hu) │
Bahasa Indonesia (id) │
italiano (it) │
日本語 (ja) │
한국어 (ko) │
português (pt) │
русский (ru) │
中文(中国大陆) (zh_CN) │
導入
XML(=The Extensible Markup Language)は、W3C が推奨する、異なるシステム間で情報を交換するための言語です。情報はテキスト形式で保存しており、XHTMLのようなWebサービスでもよく使われる現代的なデータ交換方法は、XMLを基礎としています。
Free Pascal Compiler の Free Component Library(FCL)には、XMLをサポートする "XMLRead"、"XMLWrite"、"DOM" といったユニットがあります。 FCL はすでに Lazarus のデフォルトの検索パスにはいっていますので、これらのユニットを uses に追加するだけで XML に関する機能を実装できるようになります。 FCL は現時点(October / 2005 訳注たぶん2008/12時点も同じ)で文書化されていませんので、このチュートリアルでは、これらのユニットを使った XML へのアクセス方法を紹介します。
XML の Document Object Model(DOM) は、異なる言語やシステム間で XML を利用するために同じようなインターフェースを提供する、標準化されたオブジェクトの集合です。
標準化は、メソッド、プロパティ、オブジェクトの他へのインターフェース部分についてのみ行われており、色々な言語のために、実装方法は自由になっています。 FCL では現在、完全に DOM 1.0 をサポートしています.
(訳注注:日本語版 wiki で "XML" の項目が "ネットワーク" に書かれていたときの訳注をそのまま乗せています。訳注: XML , DOM を「完全に」パースする、という実装は、かなり大変なことです。また、異機種間での利用を目的に作られていることに注意しましょう。 Lazarus は、クロスコンパイル環境であり、これを標準で持っている事は、とても便利に快適にプログラムが出来ると思われます。日本語がどこまで検証されているのか、というところは、訳からはちょっと分かりませんが...。)
利用例
以下に、 XML データの利用例を記述しています。徐々に複雑な内容を説明していきます。
textノードを読み込む
Delphi プログラマーの皆さん: TXMLDocument を用いるときには注意して下さい。ノード内のテキストは個々の TEXT ノードと扱われるため、個々のノードとしてノードのテキスト値を取得する必要があります。
別の方法として、 TextContent プロパティを用いて、与えられた一つのノード以下の全てのテキストノードを一つにまとまって取得することもできます。
ReadXMLFile プロシージャは、いつも新しい TXMLDocument を生成するため、あらかじめ TXMLDocument を生成する必要はありません。 しかし、不要になったときには、 Free をコールして document を確実に破棄して下さい。
例えば、以下のxmlファイルについてアクセスする場合、
注:全てのxmlサンプル類、サンプルコードは、UTF-8で保存する必要があるようです。また、以下の例では文頭に"半角スペース"を記入して、整形済みテキストとして表示しており、ほとんどのコードはそのまま動きますが、<?xml><sample>は先頭の半角スペースを削除しないとうまく動きませんでした。 <XML> <?xml version="1.0"?>
<sample name="s1" >sampleXML <group name="g1" >G1 <user>123</user> <item val="int" >999</item> <item val="str" >abc</item> </group> <group name="g2" >G2 <item val="str" >def</item> </group> <gr name="gr1" >GR1 <item val="str" >def</item> </gr> </sample></XML>
以下のコードで、テキストノードから値を取得する場合の、正しい方法と間違った方法をお見せします。
注: 既存のコードがうまく動かなかったので(おそらく私のコンソールアプリの理解不足です)、全てのコードをGUIコードとして作り直しました。 (動作確認は、windowsXPSP2 上の lazarus0.9.26 で行いました。)
はじめの設定
1.usesに XMLRead, Domを追加
2.form1にmemo1,button1,OpenDialogを追加 <delphi> procedure TForm1.Button1Click(Sender: TObject);
var tmpNode: TDOMNode; Doc: TXMLDocument; begin // Doc := TXMLDocument.Create;//ここで生成する必要はありません。 Memo1.Lines.Clear; // xml fileを読み込みます。 Opendialog1.Execute; ReadXMLFile(Doc,Opendialog1.FileName); // "password" ノードを取得します。 tmpNode := Doc.DocumentElement.FindNode('group'); // 選択したノードの値の書き出し Memo1.Lines.Add(tmpNode.NodeValue);// 空白値""しか取得できません。 // (ノードのテキスト値は、実際は個々の小ノードになっています。) Memo1.Lines.Add(tmpNode.FirstChild.NodeValue);// "abc" が取得できます。 // 他の方法 Memo1.Lines.Add(tmpNode.TextContent); // 最後に、documentを破棄します。 Doc.Free; end;</delphi>
出力は以下のようになります。
G1 G1 123999abc
ノードの名前を出力する
ノードに順番にアクセスする場合、 FirstChild と NextSibling プロパティ(前方から繰り返し), または LastChild と PreviousSibling (後方から繰り返し)を用いればよいでしょう。
ランダムアクセスには、ChildNodes または GetElementsByTagName メソッドが使えますが、これらは 最終的に破棄する必要のある TDOMNodeList オブジェクトを生成します。
これは、他の DOM 実装(例えばMXSML) とは異なりますが、この理由は、FCL での実装がobject-basedであり、interface-based では無いためです。
以下に、 form 上の TMemo にノードの名前を出力するサンプルを示します。 (訳注:元々の例は、上側の例と異なっているため(ファイル名がtestoなど)、記述を上記例に統一しました。)
以下のxmlファイルにアクセスする場合、
<xml> <?xml version="1.0"?>
<sample name="s1" >sampleXML <group name="g1" >G1 <user>123</user> <item val="int" >999</item> <item val="str" >abc</item> </group> <group name="g2" >G2 <item val="str" >def</item> </group> <gr name="gr1" >GR1 <item val="str" >def</item> </gr> </sample></xml>
はじめの設定
1.usesに XMLRead, Dom を追加
2.form1にmemo1,button1,OpenDialogを追加
1a.FirstChild と NextSibling プロパティを使用(順番にアクセス) (訳注:英語版のコードは上記ノートと利用プロパティ、メソッドに整合性が無いため修正しました。) <delphi> procedure TForm1.Button1Click(Sender: TObject);
var Child: TDOMNode; Doc: TXMLDocument; cnt: Integer; begin Memo1.Lines.Clear; Opendialog1.Execute; ReadXMLFile(Doc,Opendialog1.FileName); // FirstChild プロパティの使用 Child := Doc.DocumentElement.FirstChild; while Assigned(Child) do begin cnt:=cnt+1; Memo1.Lines.Add(inttostr(cnt) + ' ' + Child.NodeName + ' ' + Child.NodeValue); // NextSibling プロパティの使用 Child := Child.NextSibling; end; Doc.Free; end;</delphi>
出力は以下のようになります。
1 #text sampleXML 2 group 3 group 4 gr
1b.ChildNodes と getElementsByTagName メソッドを使用(ランダムアクセス) 注)DOMNodeListのノード数はCountとなる。
<delphi> procedure TForm1.Button1Click(Sender: TObject);
var tmpNodes: TDOMNodeList; Doc: TXMLDocument; i: Integer; begin Memo1.Lines.Clear; Opendialog1.Execute; ReadXMLFile(Doc,Opendialog1.FileName); // ChildNodes メソッドの使用 tmpNodes:=Doc.DocumentElement.ChildNodes; Memo1.Lines.Add('1.ChildNodes count=' + inttostr(tmpNodes.Count)); if(tmpNodes.Count <> 0) then for i:=0 to tmpNodes.Count-1 do begin Memo1.Lines.Add(inttostr(i) + ' ' + tmpNodes[i].NodeName + ' ' + tmpNodes[i].NodeValue); end; tmpNodes.Free; // getElementsByTagName メソッドの使用 tmpNodes:= Doc.GetElementsByTagName('group'); Memo1.Lines.Add('2.getEBTN count=' + inttostr(tmpNodes.Count)); if(tmpNodes.Count <> 0) then for i:=0 to tmpNodes.Count-1 do begin Memo1.Lines.Add(inttostr(i) + ' ' + tmpNodes[i].NodeName + ' ' + tmpNodes[i].NodeValue); end; tmpNodes.Free; end;</delphi>
出力は以下のようになります。
1.ChildNodes count=4 0 #text sampleXML 1 group 2 group 3 gr 2.getEBTN count=2 0 group 1 group
2.ChildNodes メソッドを用いた順次アクセスのコードは以下のようになります。 (注:日本語版の"ネットワーク"項目のXMLが記述されていたコードがあったので、とりあえず入れておきます。) <delphi> procedure TForm1.Button1Click(Sender: TObject);
var Doc: TXMLDocument; i, j: Integer; begin Memo1.Lines.Clear; Opendialog1.Execute; ReadXMLFile(Doc,Opendialog1.FileName); // ChildNodes メソッドの使用 with Doc.DocumentElement.ChildNodes do begin for i := 0 to (Count - 1) do begin Memo1.Lines.Add('i' + inttostr(i) + Item[i].NodeName + ' ' + Item[i].NodeValue); for j := 0 to (Item[i].ChildNodes.Count - 1) do begin Memo1.Lines.Add(' j' + inttostr(j) + ' ' + Item[i].ChildNodes.Item[j].NodeName + ' ' + Item[i].ChildNodes.Item[j].NodeValue); end; end; end; Doc.Free; end;</delphi>
出力は以下のようになります。
i0#text sampleXML i1group j0 #text G1 j1 user j2 item j3 item i2group j0 #text G2 j1 item i3gr j0 #text GR1 j1 item
XML を TreeView で表示する
XMLファイルの一般的な利用として、XMLファイルの内容を TreeView の形で表示することがあります。 Lazarusの ”Common Controls” タブに TTreeView コンポーネントがあります。
以下の手続きは、既にファイルから読み込んだか、コードの中で生成するかした XML 文書を取得し、その内容をTreeViewの形で表示します。それぞれのノードがもつキャプションが、最初の属性の内容になります。
はじめの設定
1.usesに XMLRead, Dom を追加
2.form1にTTreeView1を追加
3.TForm1のtypeにメソッド”procedure XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument); ”を追加 <delphi> procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);
var iNode: TDOMNode; procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode); var cNode: TDOMNode; begin if Node = nil then Exit; // Stops if reached a leaf // Adds a node to the tree TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue); // Goes to the child node cNode := Node.FirstChild; // Processes all child nodes while cNode <> nil do begin ProcessNode(cNode, TreeNode); cNode := cNode.NextSibling; end; end; begin iNode := XMLDoc.DocumentElement.FirstChild; while iNode <> nil do begin ProcessNode(iNode, nil); // Recursive iNode := iNode.NextSibling; end; end;</delphi>
XML 書類を修正する
まず、TDOMDocuments は DOM へのハンドルであることを覚えてください。このクラスのインスタンスは、インスタンスを作成 (create) してもXML文書をロードしても得ることができます。
一方、ノードは通常のオブジェクトのようには生成することができません。必ず TDOMDocument クラスのメソッドを用いてください。その後、別のメソッドによって、新たに生成したノードをツリーの中の然るべき場所に置きます。これはノードというものがDOMの中の特定の文書によって「所有」される必要があるからです。
以下に、TDOMDocumentのいくつかの普通のメソッドを示します。
<delphi> function CreateElement(const tagName: DOMString): TDOMElement; virtual;
function CreateTextNode(const data: DOMString): TDOMText; function CreateCDATASection(const data: DOMString): TDOMCDATASection; virtual; function CreateAttribute(const name: DOMString): TDOMAttr; virtual;</delphi>
次は、選択されたアイテムを TTreeView の中に置き、そのツリーが表す XML 文書の中に子ノードを挿入するメソッドの例です。XML2Tree function によってあらかじめ XML の全ての内容を TreeView に含めておく必要があります。
<delphi> procedure TForm1.actAddChildNode(Sender: TObject); var
position: Integer; NovoNo: TDomNode;
begin
// 選択された要素を発見する if TreeView1.Selected = nil then Exit;
if TreeView1.Selected.Level = 0 then begin position := TreeView1.Selected.Index;
NovoNo := XMLDoc.CreateElement('item'); TDOMElement(NovoNo).SetAttribute('nome', 'Item'); TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo'); with XMLDoc.DocumentElement.ChildNodes do begin Item[position].AppendChild(NovoNo); Free; end;
// TreeView を更新する TreeView1.Items.Clear; XML2Tree(TreeView1, XMLDoc); end else if TreeView1.Selected.Level >= 1 then begin {******************************************************************* * この手続きはツリーの最初のレベルでしかうまく働かないが、 * ツリーの全ての深さに適用できるよう改造するのは容易である。 *******************************************************************} end;
end;</delphi>
TXMLDocument を一つの文字列から生成する
MyXmlString がある XML ファイルの内容を含んだものだとすると、次のコードによって DOM を生成することができます。
<delphi>Var
S : TStringStream; XML : TXMLDocument;
begin
S:= TStringStream.Create(MyXMLString); Try S.Position:=0; XML:=Nil; ReadXMLFile(XML,S); // Complete XML document // Alternatively: ReadXMLFragment(AParentNode,S); // Read only XML fragment. Finally S.Free; end;
end;</delphi>
文書の検証
2007年3月以降、DTDの有効性を検証する機能が FCL XMLパーザに加わっています。文書の論理構造があらかじめ定義された Document Type Definition (DTD) と呼ばれる規則に則っている場合、有効です。
次は DTD 付きの XML 文書の例です:
<xml> <?xml version='1.0'?>
<!DOCTYPE root [ <!ELEMENT root (child)+ > <!ELEMENT child (#PCDATA)> ]> <root> <child>This is a first child.</child> <child>And this is the second one.</child> </root></xml>
この DTD はルートとなる要素が一つまたは複数の「子」ををち、それらの「子」は内部に文字データのみを持っていることを示しています。パーザが何か違反を発見した場合、レポートされます。
この種の文書をロードするのは若干複雑になっています。TStream オブジェクトに XML データが含まれているとしましょう:
<delphi>procedure TMyObject.DOMFromStream(AStream: TStream); var
Parser: TDOMParser; Src: TXMLInputSource; TheDoc: TXMLDocument;
begin
// パーザオブジェクトを生成する Parser := TDOMParser.Create; // 入力ソースオブジェクトも Src := TXMLInputSource.Create(AStream); // 検証しなくちゃ Parser.Options.Validate := True; // 報告を受け取るためのエラーハンドラを関連づける Parser.OnError := @ErrorHandler; // やっと仕事ができる Parser.Parse(Src, TheDoc); // ...後始末もできる。 Src.Free; Parser.Free;
end;
procedure TMyObject.ErrorHandler(E: EXMLReadError); begin
if E.Severity = esError then // 検査エラー以外関心がない writeln(E.Message);
end;</delphi>
XML ファイルの作成
以下はXML ファイルに書き込む完全なコードです(DeveLazarusブログのチュートリアルから)。Uses 節に DOM と XMLWrite を入れ忘れないでください。
<delphi>unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, DOM, XMLWrite;
type
{ TForm1 } TForm1 = class(TForm) Button1: TButton; Label1: TLabel; Label2: TLabel; procedure Button1Click(Sender: TObject); private { private declarations } public { public declarations } end;
var
Form1: TForm1;
implementation
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject); var
xdoc: TXMLDocument; // 文書を格納する変数 RootNode, parentNode, nofilho: TDOMNode; // ノードを格納する変数
begin
//文書を作成する xdoc := TXMLDocument.create;
//ルートノードを作成する RootNode := xdoc.CreateElement('register'); Xdoc.Appendchild(RootNode); // ルートノードを保存する
//親ノードを作成する RootNode:= xdoc.DocumentElement; parentNode := xdoc.CreateElement('usuario'); TDOMElement(parentNode).SetAttribute('id', '001'); // 親ノードを示す属性を作成する RootNode.Appendchild(parentNode); // 親ノードを保存する
//子ノードを作成する parentNode := xdoc.CreateElement('nome'); // 子ノードを一つ作成する //TDOMElement(parentNode).SetAttribute('sexo', 'M'); // 属性を作成する nofilho := xdoc.CreateTextNode('Fernando'); // ノードに値を挿入する parentNode.Appendchild(nofilho); // ノードを保存する RootNode.ChildNodes.Item[0].AppendChild(parentNode); // 子ノードをそれぞれの親ノードに挿入する
//子ノードを作成する parentNode := xdoc.CreateElement('idade'); // 子ノードを一つ作成する //TDOMElement(parentNode).SetAttribute('ano', '1976'); // 性を作成する nofilho := xdoc.CreateTextNode('32'); // ノードに値を挿入する parentNode.Appendchild(nofilho); // ノードを保存する .ChildNodes.Item[0].AppendChild(parentNode); // 子ノードをそれぞれの親ノードに挿入する
writeXMLFile(xDoc,'teste.xml'); // XML に書く Xdoc.free; // メモリを解放する
end;
initialization
{$I unit1.lrs}
end.</delphi>
The result will be the XML file below: <XML><?xml version="1.0"?> <register>
<usuario id="001"> <nome>Fernando</nome> <idade>32</idade> </usuario>
</register></XML>
--Fernandosinesio 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com
文字コード
XMLの標準によると、XMLの最初の行にはエンコーディングを示す属性を置いても置かなくてもかまいません。Lazarus 0.9.26 では、TXMLDocumentにエンコーディングを示すプロパティがありますが、無視されます。手続き writeXMLFile は常に UTF-8 を用い、最初の行にエンコーディング属性を作成することはありません。
External Links
- W3Schools Xml Tutorial
- Thomas Zastrow article FPC and XML