XML Tutorial/ja

From Lazarus wiki
Revision as of 09:01, 9 August 2011 by Chronos (talk | contribs)
Jump to navigationJump to search

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)

日本語版メニュー
メインページ - Lazarus Documentation日本語版 - 翻訳ノート - 日本語障害情報


導入

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

ノードの名前を出力する

ノードに順番にアクセスする場合、 FirstChildNextSibling プロパティ(前方から繰り返し), または LastChildPreviousSibling (後方から繰り返し)を用いればよいでしょう。

ランダムアクセスには、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.FirstChildNextSibling プロパティを使用(順番にアクセス) (訳注:英語版のコードは上記ノートと利用プロパティ、メソッドに整合性が無いため修正しました。) <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.ChildNodesgetElementsByTagName メソッドを使用(ランダムアクセス) 注)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


Multithreaded Application Tutorial