Delphi 6 XML映象工具

三、Delphi 6 XML映象工具
    本文是三篇论述Delphi 6中XML功能系列文章的第三篇,也是最后一篇,论述Delphi 6中的XML映象工具,也称之为XML映象器(XML Mapper)。
Delphi 6 含有许多更新更强的XML支持功能,增加了XML文件编程,XML数据绑定向导,XML映象和BizSnap(SOAP/XML Web服务)。本文是三篇论述Delphi 6中XML功能系列文章的第三篇,也是最后一篇,论述Delphi 6中的XML映象工具,也称之为XML映象器(XML Mapper)。

XML映象器
可以单独打开或者在IDE的工具(Tools)菜单中打开映象器(XML Mapper)。这个工具将XML文件影射为类似于数据库中的数据集(DataSet)!在前二篇文章中,我们都使用TXMLDocument组件来处理XML文件或XML绑定的数据。现在,我们不再使用TXMLDocument组件了。我们用XML映象器将XML文件转换成数据包(或反之),这样就可以象我们非常熟悉得那样使用诸如DataSet这样的组件来处理XML数据。
XML映象器可以打开XML文件和数据包文件(*.xml,*.cds),纲文件(*.dtd,*.xdr,*.xsd),库文件(*.xrp)和转换文件(*.xtr)。后面这个转换文件是由映象器自行产生的。现在加载我们在前二篇文章中用到的Clinic.xml文件,看看数据影射成什么样。

如图1所示,Clinic.xml影射为一个Clinics结构,含有若干Clinic子项。每个Clinic有4个域:@No, Title, Date, 和Topics。注意@No是属性,其他3个是元素。
将鼠标移至Clinic节点,按右键,选择"Select All"(Ctrl+A)或者"Select All Children",这样就把@No, Title, Date and Topics各项加到中间的映象表内。此时还没有生成相应的数据集域。
我们再次到Clinic节点按下鼠标右键,选择"Create Datapacket from XML"(Ctrl+D),这时产生了相应的数据包并显示在映象器的右方,如图2所示。
映象器将XML文件转换成4个标准格式的数据包。我们还可以增加或删除数据包。这在B2B场合尤为重要,将接收到的数据(纪录)插入到数据库中。

创建并测试转换
按下Create and Test Transformation按钮,随即弹出一个DBGrid窗口,以ClientDataSet数据包格式显示Clinic.xml文件里的数据。数据转换之后,有4种方式存盘。对于初学者来说,可以存回原先的Clinic.xml文件。另外还可以存为包数据文件格式(*.xml),库文件(repository)格式(.xrp)和转换文件格式(*.xtr)。后面的库文件可以被XMLTransform组件(在Data Access标签里)调用。

XML转换
Delphi 6里有3个XML转换组件。TXMLTransformProvider组件通过clinic.xtr文件将原始XML文件转换到ClientDataSet。
举例来说,在Delphi 6的主窗体上安放一个TXMLTransformProvider(在Data Access标签里)。将TransformRead属性的TransformationFile子属性设为clinic.xtr作为数据源。将XMLDataFile设为clinic.xml文件。
然后安放一个ClientDataSet组件,将它的ProviderName属性设为XMLTransformProvider。
现在可以激活ClientDataSet组件了,它将由XMLTransformProvider提供的数据转换过来。它的PacketRecords属性值缺省为-1,表示转换全部XML文件。
要显示数据,我们再加入DataSource和DBGrid组件。将DataSource指向ClientDataSet,DBGrid指向DataSource,就象我们通常使用得那样。结果显示如图3。

现在可以象使用数据库数据那样使用XML文件里的数据了。如果对数据进行了修改,可以调用ClientDataSet.ApplyUpdates方法将改动的数据存回XML文件。
但是这样做是不够的。我们还得启动XML映象器,将创建方式由"XML to Datapacket"改为"Datapacket to XML"。这时如果按下"Create and Test Transformation"键,看到的是空白页。然后将转换结果存为ClinicToXml.xtr文件。
现在,将TXMLTransformationProvider的TransformationWrite属性中的子属性TransformationFile设为ClinicToXml.xtr文件。
最后在主窗体的OnClose事件中加上存盘指令:
  ClientDataSet1.ApplyUpdates(-1)
程序退出之前会将作出的数据修改存回XML文件。
这样就为Delphi 6的B2B开发环境BizSnap的虚拟中间层作好了数据准备。

想看图: http://www.gois.ws/showfile.asp?id=53:default
 
 
少爷 (2002-10-22 9:00:00)  
[red][b][center]2、Delphi 6 XML数据绑定[/center][/b][/red]

[blue][b]本文是三篇论述Delphi 6中XML功能系列文章的第二篇,论述Delphi 6中的XML数据绑定(XML Data Binding)。 [/b][/blue]
Delphi 6 含有许多更新更强的XML支持功能,增加了XML文件编程,XML数据绑定向导,XML映象和BizSnap(SOAP/XML Web服务)。我在上一篇文章论述了Delphi 6中的XML文件编程(XML Document Programming)。本文是三篇论述Delphi 6中XML功能系列文章的第二篇,论述Delphi 6中的XML数据绑定(XML Data Binding)。

XML文件编程
XMLDocument组件让我们能够遍历和编辑XML文件。但是在上一篇文章中我提到了,我们只能与无类型节点打交道,必须记住节点元素的名字。这意味着无法进行实时编译调试!幸亏的是,如果Delphi只能处理这样简单的问题就不成其为Delphi了。运用XML的内容相关结构可以做更高级的应用,这就是Delphi 6的XML数据绑定向导(XML Data Binding Wizard)。

XML数据绑定
在Delphi 6的模块仓库(Object Repository)中可以找到XML数据绑定向导(XML Data Binding Wizard)。程序员能够用它生成相应的接口和类来访问与修改XML文件数据,诸如ClientDataSetXML数据,ADO XML数据,其它XML文件数据(如我们在前文用到的Clinic.xml,本文继续使用这个简单的XML文件作示例)。

现在开始吧,启动Delphi 6,在主菜单上选择File | New - Other,然后在仓库中选择XML Data Binding,如图1所示。

向导有三个页面。第一页定义XML纲(Schema)或XML文件(本例用Clinic.xml),如图2所示。
在资源输入框内输入XML纲(Schema)或XML文件。“选项”(Options)对话框定义编码选项和数据类型映射关系(Data Type map)。以后我们还会谈到这些选项。

向导的第二页显示了树结构和节点数据类型(亦即向导生成了些什么样的代码)。图3可以看到我的XML文件结构。
可以看到XML文件里描述的嵌套节点(ClinicsType与ClinicType)和单节点(String)。这时可以打开选项(Options)对话框(图4),修改编码(比如修改前缀)和数据类型映射。

向导的第三页显示生成的相应接口和类。可以把这些结果保存到文件(例如生成Clinic.xdb)。
结果(存储为Clinic.xdb文件)显示如下。我们得到一个ClinicsType类型的Clinics元素,其中包括ClinicType类型的Clinic系列元素。

  <?xml version="1.0"?>
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xdb="http://www.borland.com/schemas/delphi/6.0/XMLDataBinding">
    <xs:element name="Clinics" type="ClinicsType"/>
    <xs:complexType name="ClinicsType">
      <xs:annotation>
        <xs:appinfo xdb:docElement="Clinics"/>
      </xs:annotation>
      <xs:sequence>
        <xs:element name="Clinic" type="ClinicType" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
    <xs:complexType name="ClinicType">
      <xs:sequence>
        <xs:element name="Title" type="xs:string"/>
        <xs:element name="Date" type="xs:string"/>
        <xs:element name="Topics" type="xs:string"/>
      </xs:sequence>
      <xs:attribute name="No" type="xs:string"/>
    </xs:complexType>
  </xs:schema>

文件同时定义ClinicType类型的Clinic元素包含一系列字符串元素(Title, Date和Topics)。

生成代码
向导生成的代码可以直接在应用中使用。不幸的是,Delphi 6有时会产生“非法操作”的出错信息。重新执行一遍,它又能工作了。
以下是生成的代码(Clinic.pas):

  {****************************************************}
  {                                                    }
  {              Delphi XML Data Binding               }
  {                                                    }
  {         Generated on: 2001/11/07 00:37:00          }
  {       Generated from: D:/D6Clinic/src/Clinic.xml   }
  {   Settings stored in: D:/D6Clinic/src/Clinic.xdb   }
  {                                                    }
  {****************************************************}
  unit Clinic;
  interface
  uses xmldom, XMLDoc, XMLIntf;

  type

  { Forward Decls }

    IXMLClinicsType = interface;
    IXMLClinicType = interface;

  { IXMLClinicsType }

  IXMLClinicsType = interface(IXMLNodeCollection)
    ['{06723E03-662D-11D5-81CE-00104BF89DAD}']
    { Property Accessors }
    function Get_Clinic(Index: Integer): IXMLClinicType;
    { Methods & Properties }
    function Add: IXMLClinicType;
    function Insert(const Index: Integer): IXMLClinicType;
    property Clinic[Index: Integer]: IXMLClinicType
      read Get_Clinic; default;
  end;

  { IXMLClinicType }

  IXMLClinicType = interface(IXMLNode)
    ['{06723E04-662D-11D5-81CE-00104BF89DAD}']
    { Property Accessors }
    function Get_No: WideString;
    function Get_Title: WideString;
    function Get_Date: WideString;
    function Get_Topics: WideString;
    procedure Set_No(Value: WideString);
    procedure Set_Title(Value: WideString);
    procedure Set_Date(Value: WideString);
    procedure Set_Topics(Value: WideString);
    { Methods & Properties }
    property No: WideString read Get_No write Set_No;
    property Title: WideString read Get_Title write Set_Title;
    property Date: WideString read Get_Date write Set_Date;
    property Topics: WideString read Get_Topics write Set_Topics;
  end;

  { Forward Decls }

    TXMLClinicsType = class;
    TXMLClinicType = class;

  { TXMLClinicsType }

  TXMLClinicsType = class(TXMLNodeCollection, IXMLClinicsType)
  protected
    { IXMLClinicsType }
    function Get_Clinic(Index: Integer): IXMLClinicType;
    function Add: IXMLClinicType;
    function Insert(const Index: Integer): IXMLClinicType;
  public
    procedure AfterConstruction; override;
  end;

  { TXMLClinicType }

  TXMLClinicType = class(TXMLNode, IXMLClinicType)
  protected
    { IXMLClinicType }
    function Get_No: WideString;
    function Get_Title: WideString;
    function Get_Date: WideString;
    function Get_Topics: WideString;
    procedure Set_No(Value: WideString);
    procedure Set_Title(Value: WideString);
    procedure Set_Date(Value: WideString);
    procedure Set_Topics(Value: WideString);
  end;

  { Global Functions }

  function GetClinics(Doc: IXMLDocument): IXMLClinicsType;
  function LoadClinics(const FileName: WideString): IXMLClinicsType;
  function NewClinics: IXMLClinicsType;

这里有二个接口类型:IXMLClinicsType和IXMLClinicType;用二个类(TXMLClinicsType和TXMLClinicType)来执行这二个接口。另外还有三个全局函数:GetClinics (获得根元素),LoadClinics (从外部XML文件加载)和NewClinics (在内存生成新文件)。

用法
使用生成的Clinic.pas单元是很容易的。跟前一篇文章的做法一样,使用XMLDocument组件(在Inernet标签内)。不过我们不再使用无类型节点了,我们可以调用GetClinics函数获得IXMLClinicsType类型。以下是具体操作过程:

在Delphi 6建立一个新的应用(project)
在XML数据绑定向导指引下建立Clinic.pas文件(经过命名存盘 - 译者)
在主窗体上加入一个XMLDocument组件,其FileName属性为Clinic.xml
在主窗体的OnCreate事件中加入以下代码:
  procedure TForm1.FormCreate(Sender: TObject);
  var
    Clinics: IXMLClinicsType;
  begin
    Clinics := GetClinics(XMLDocument1);
  end;

把Clinics变量放到主窗体中是很有用的,这样就可以在主窗体运行期间使用Clinics接口。使用IXMLClinicsType变量类型要比以前使用普通XMLDocument组件方便多了。现在可以通过Get_Clinic方法来获得各个Clinic元素,还可以在特定位置插入新的Clinic元素。用Clinics.Clinic可以获得节点元素,用Getter和Setter方法可以得到或设置元素值。现在可以直接访问No, Title, Date, Topics等属性了:
  procedure TForm1.ButtonGetClick(Sender: TObject);
  var
    Clinic: IXMLClinicType;
  begin
    Clinic := Clinics.Clinic[0];
    EditNo.Text := Clinic.No;
    EditTitle.Text := Clinic.Title;
    EditDate.Text := Clinic.Date;
    EditTopics.Text := Clinic.Topics
  end;

可以在Clinic.pas中看到,Getter和Setter是方法而不是属性(实际上,我始终认为使用属性更清楚些)。但是Delphi 6让你看到的却是属性描述而不是方法本身(Delphi 6的另一个受欢迎的优点)。将上面这段代码与前一篇文章使用的方法相比较,就能感到操作方便多了。

下面的例子在XML树的末尾加入一个节点:
  procedure TForm1.ButtonAddClick(Sender: TObject);
  begin
    with Clinics.Add do
    begin
      No := '2001-2-8;  // 8th Clinic of the 2nd series of 2001
      Title := 'Special Kylix 2 Clinic';
      Date := '2001/12/21';
      Topics := 'Kylix 2 New Features'
    end
  end;

如果没有把XMLDocument组件的AutoSave设置为真,可以用以下方法保存更动结果:
  procedure TForm1.FormDestroy(Sender: TObject);
  begin
    XMLDocument1.SaveToFile;
  end;

这就是XML数据绑定向导,一个非常方便的方法。它能做得越来越好。

下一篇文章:
我们已经看到了XML数据绑定的优点。不过好象还有点不“满足”,比方要遍历各个节点,存取节点值(不单单是字符串类型),虽然可用选项决定,但还是用Delphi 6的XML映象更好,它的功能更强。我们将在下一篇文章论述。

图: http://www.gois.ws/showfile.asp?id=52:default
 
少爷 (2002-10-22 9:07:00)  
[center][red][b][h3]一、Delphi 6 XML文件编程[/h3][/b][/red][/center]

[quote][b][blue][h4]Delphi 6提供的XML功能增加了XML文件编程,XML数据绑定向导,XML映象和BizSnap(SOAP/XML Web服务)。本文是三篇论述Delphi 6中XML功能系列文章的第一篇,论述Delphi 6中的XML文件编程。[/h4][/blue][/b][/quote]

Delphi 6 含有许多更新更强的XML支持功能。Delphi早期版本中提到的XML只在说明MIDAS数据格式和XMLBroker组件(在ClientDataSet和ADODataSet组件中也提到过)。
Delphi 6提供的XML功能增加了XML文件编程,XML数据绑定向导,XML映象和BizSnap(SOAP/XML Web服务)。本文是三篇论述Delphi 6中XML功能系列文章的第一篇,论述Delphi 6中的XML文件编程(XML Document Programming)。

XML文件
就在前几年XML刚刚问世的时候,它就被赋予电子商务的世界语的美称,尤其在B2B(business-2-business)领域内更是如此。原因就是XML是个非常简单的而又是结构化的ASCII文本语言,任何人和任何事都能读懂它。与作为当前网页标准格式的HTML语言不一样。两者之间的不同之处在于:HTML要用预先定义的语法集来解释;XML文件虽然要遵循通用的语法规则,但真正的关键词(标记)则是由作者决定的。其含义可由DTD(Document Type Definition)文件或者现在更为流行使用的纲(Schema)来选择定义。使用DTD或Schema的XML文件可以说是一个自我解释的文件,在数据集成化和出错调整时都很有用。

XML的结构化模式使得数据和信息可以在应用程序之间相互传递(包括在多层应用的中间层之间,例如Delphi提供的功能)。它提供一个标准格式,对所用的通讯协议是透明的。这就是XML在电子商务应用的电子数据交换(Electronic Data Interchange, EDI)和B2B中扮演重要角色的原因。通过DTD或者Schema来“定义”(或检查)XML文件理所当然地使得任何人都可以与任何其它人和事交谈,只要使用恰当的的XML/DTD/Schema组合。现在越来越经常要求集成现有的系统,XML可能成为系统之间相互交谈和理解的“语言”。
本文不讨论涉及EDI或B2B的XML。但它引出三篇论及XML文件编程和Delphi 6的XML新特性的文章。

XML文件编程
Delphi 6 支持DOM解析,因此我们可以读(和翻译!)以及编辑任何XML文件,甚至在缺少了DTD或Schema的情况下,一如本文所示。我在本文创建了一个包含我的网页内容的小小的XML文件:
  <?xml version="1.0" standalone="yes" ?>
  <Clinics>
    <Clinic No="2002-1">
      <Title>dbExpress and DataSnap</Title>
      <Date>2002/01/10</Date>
      <Topics>  </Topics>
    </Clinic>
    <Clinic No="2002-2">
      <Title>WebBroker/InternetExpress and WebSnap</Title>
      <Date>2002/01/31</Date>
      <Topics>  </Topics>
    </Clinic>
    <Clinic No="2002-3">
      <Title>WebSnap and Adapters</Title>
      <Date>2002/02/21</Date>
      <Topics>  </Topics>
    </Clinic>
    <Clinic No="2002-4">
      <Title>BizSnap and WebServices</Title>
      <Date>2002/03/14</Date>
      <Topics>  </Topics>
    </Clinic>
    <Clinic No="2002-5">
      <Title>WebSnap and BizSnap</Title>
      <Date>2002/04/04</Date>
      <Topics>  </Topics>
    </Clinic>
  </Clinics>

这个XML文件将作为这组系列文章的例子(本文以及后面的XML数据绑定和XML映像等文章里都会用到)。

TXMLDocument组件
要进行XML文件编程就要用到TXMLDocument组件(在Delphi 6组件栏的Internet标签里)。可以从那里拖拽一个组件到窗体或数据模块上。TXMLDocument组件有些值得一提的属性。显然,Active属性可以用于打开XML文件,但我们这会儿还用不上。DOMVendor属性定义了XML文件的解析器。在我的机器上,被置为"MSXML"。可以自己插入其它类型的DOMVendor(实际就是任何能够执行IDOMImplementation的接口组件)。在使用自己的DOMVendor组件前,要对它先进行注册。DOMVendor是一个全局变量,含有一个DOMVendor注册值表。这样第三方的DOMVendor可以在注册后加入这个表,让用户可以自行选择TXMLDocument的DOMVendor属性值。
TXMLDocument的第三个属性是FileName,指向XML文件(本例为clinics.xml)。如果XML文件并不是直接存在,而是通过其它转换获得,也可以设置这个属性值[译者注:只要使用的解析器支持,甚至可以指向一个URL]。这是在EDI和B2B里的基本用法,这种场合下往往不必[有时也不可能 - 译者]存储真正的XML文件。
NodeIndentStr属性决定子节点的后移位置,缺省值为二个空格,最多可选到八个空格。这个属性只有在选项里设置了doNodeAutoIndent标识为真(True)后才有效,这个标识的缺省状态为非真(False)。组件选项里还有其它标识,如NodeAutoCreate, AttrNull, AutoPrefix, NamespaceDecl和AutoSave。AutoSave使得组件在关闭时能够将XML文件发生的变动自动存盘。我觉得这是个非常好的特性,所以在我的例子里,我把它设置为真(它的缺省状态是非真)。
除了常规选项之外,XMLDocument组件还有解析选项,如ResolveExternals, ValidateOnParse, PreserveWhiteSpace和AsyncLoad,这些标识的意义从名字上就可以看出。
XMLDocument组件的最后一个属性是XML,指向一条XML的字符串(点击属性旁边的省略号可以编辑XML)。正如我前面说过的,这在B2B多层应用中是非常有用的。这时中间层往往接收从其它应用中得到XML字符串,就要对这些XML数据进行处理或“编程”。

XML文件编程
只要稍作设置(设置FileName为clinics.xml和将doAutoSave置为真),就可以激活TXMLDocument组件。激活之后,就可以遍历节点,读取和编辑数据。
现在我们可以在XMLDocument里访问各个节点(IXMLNode),递归访问各节点的子节点。例如,可以用一个按钮来获取第一个节点的元素值并将结果写入Memo:
  procedure TForm1.Button1Click(Sender: TObject);
  var
    Clinic: IXMLNode;
  begin
    Clinic := XMLDocument1.DocumentElement.ChildNodes[0];
    Memo1.Lines.Clear;
    Memo1.Lines.Add(Clinic.ChildNodes['Title'].Text);
    Memo1.Lines.Add(Clinic.ChildNodes['Date'].Text);
    Memo1.Lines.Add(Clinic.ChildNodes['Topics'].Text)
  end;

增加索引指针可以访问其它节点。下面的例子用"current"作索引,通过循环增加"current"的值来遍历节点。我用try-except来防止循环出界。
  procedure TForm1.Button2Click(Sender: TObject);
  var
    Clinic: IXMLNode;
  begin
    Inc(current);
    try
      Clinic := XMLDocument1.DocumentElement.ChildNodes[current];
      Memo1.Lines.Clear;
      Memo1.Lines.Add(Clinic.ChildNodes['Title'].Text);
      Memo1.Lines.Add(Clinic.ChildNodes['Date'].Text);
      Memo1.Lines.Add(Clinic.ChildNodes['Topics'].Text);
    except
      on E: Exception do
        Memo1.Lines.Add(E.Message)
    end
  end;

除了遍历节点之外,还可以修改子节点的值。下面的代码将第一个节点的标题值加上一个"HOT"前缀。
  procedure TForm1.Button1Click(Sender: TObject);
  var
    Clinic: IXMLNode;
  begin
    current := 0;
    Clinic := XMLDocument1.DocumentElement.ChildNodes[current];
    Memo1.Lines.Clear;
    Clinic.ChildNodes['Title'].Text := 'HOT: ' + Clinic.ChildNodes['Title'].Text;
    Memo1.Lines.Add(Clinic.ChildNodes['Title'].Text);
    Memo1.Lines.Add(Clinic.ChildNodes['Date'].Text);
    Memo1.Lines.Add(Clinic.ChildNodes['Topics'].Text);
  end;

还可以增删节点。下面的代码增加一个子节点并赋予缺省值:
  procedure TForm1.Button1Click(Sender: TObject);
  var
    Clinic: IXMLNode;
  begin
    Clinic := XMLDocument1.DocumentElement.AddChild('Clinic');
    Clinic.ChildNodes['Title'].Text := 'Title';
    Clinic.ChildNodes['Date'].Text := 'Date';
    Clinic.ChildNodes['Topics'].Text := 'Topics';
  end;

记住,我们已经将AutoSave设置为真。所以在XMLDocument对象或应用关闭时,任何变化都将自动存盘。也可以调用XMLDocument1.SaveToFile方法显式存盘。

下一篇文章:XML数据绑定
XMLDocument让我们能够访问XML节点并编辑节点。但我们只能与无类型节点打交道,必须记住节点元素的名字。用上面的代码可以漂亮地处理小型的XML文件,但是,Delphi如果只能处理这样简单的问题就不成其为Delphi了。运用XML的内容相关结构可以做更高级的应用,这就是Delphi 6提供的XML数据绑定特性。我们将在下一篇文章中论述。

[center][red][h3]用XML文件作数据库转换中介[/h3][/red][/center]

[quote][b][blue][h4]本文提供的Delphi代码可以将XML文件作为数据库转换的中间介质。[/h4][/blue][/b][/quote]

[quote]本文说明如何从一个Paradox数据库读取数据,生成XML文件,然后将XML文件中的数据插入到一个新的表中。这个新的数据库表可以还是Paradox,也可以是任何Delphi能够访问的数据库。我使用MSXML来解析XML文件,因此你必须使用IE 5.0以上才能使用我的代码。

生成XML文件
转换生成XML文件的过程如下。
XML文件的根名字与表名相同(本例是"country")。
标记<Records>和</Records>之间是各个纪录。
XML中节点元素标记<Field Name>和</Field Name>之间是数据。
<country>
 <Records>
   <Name>Argentina</Name>
   <Capital>Buenos Aires</Capital>
   <Continent>South America</Continent>
   <Area>2777815</Area>
   <Population>32300003</Population>
 </Records>
                    .
                    .

</country>

启动一个新应用(application),在主窗口(Main Form)中加入按钮(TButton)和表(TTable)控件。Tabled的设置如下:
DatabaseName : DBDEMOS
Name : Table1
TableName : country (不要后缀 ".db")
Active : True

选择主菜单Project/Import,导入数据库类型。
选择"Microsoft XML, Version 2.0 (version 2.0)",然后点击"Create Unit"按钮。此后,就有一个MSXML_TLB增加到你的工程(project)文件中。要在unit文件的interface段的uses说明里加上MSXML_TLB。

在变量声明中作如下声明:
DataList : TStringlist;
doc : IXMLDOMDocument;
root, child, child1 : IXMLDomElement;
text1, text2 : IXMLDOMText;
nlist : IXMLDOMNodelist;
dataRecord : String;

在执行部分加入以下代码,这就可以从country表中读取数据并生成XML文件。
function TForm1.makeXml(table:TTable):Integer;
var
  i : Integer;
  xml : String;
begin
  try
    table.close;
    table.open;
    xml  := table.TableName;
    doc  := CreateOleObject('Microsoft.XMLDOM') as IXMLDomDocument;
    // 设置XML文件的根名字为"country",与表名相同。
    root := doc.createElement(xml);
    // 把根节点加入到文件中
    doc.appendchild(root);
    // 循环产生整个XML文件
    while not table.eof do
    begin
    // 加入第一级子节点,即纪录
      child:= doc.createElement('Records');
      root.appendchild(child);
      // 加入第二级子节点
      for i:=0 to table.FieldCount-1 do
      begin
        child1:=doc.createElement(table.Fields[i].FieldName);
        child1.appendChild(doc.createTextNode(table.Fields[i].value));
        child.appendchild(child1);
      end;
    table.Next;
    end;
    doc.save(xml+'.xml');
    Result:=1;
  except
    on e:Exception do
      Result:=-1;
  end;
end;
Call the function in Button1's click event:
procedure TForm1.Button1Click(Sender: TObject);
begin
  if makeXml(table1)=1 then
    showmessage('XML Generated')
  else
    showmessage('Error while generating XML File');
end;

随后用IE浏览器打开XML文件(country.xml),就能看到如下结果:
<country>
 <Records>
  <Name>Argentina</Name>
  <Capital>Buenos Aires</Capital>
  <Continent>South America</Continent>
  <Area>2777815</Area>
  <Population>32300003</Population>
 </Records>
 <Records>
  <Name>Bolivia</Name>
  <Capital>La Paz</Capital>
  <Continent>South America</Continent>
  <Area>1098575</Area>
  <Population>7300000</Population>
 </Records>
                    .
                    .
 <Records>
  <Name>Venezuela</Name>
  <Capital>Caracas</Capital>
  <Continent>South America</Continent>
  <Area>912047</Area>
  <Population>19700000</Population>
 </Records>
</country>

生成新表
XML文件中的数据与原表的数据完全一样。如果直接将数据插入回原表,会导致主索引出错。所以如果真想这么作得话,要事先删除原表。
在主窗口上增加另一个按钮和一个memo控件。为第二个按钮填写以下onclick事件代码。(memo用于显示数据插入成功与否的信息)。
procedure TForm1.Button2Click(Sender: TObject);
var
   i,ret_val,count:Integer;
   strData:String;
begin
    // 注意:在向country表插入数据前,确认XML文件中的数据与原表数据是不同的。或者作一个新表。
    try
      count:=1;
      DataList:=TStringList.Create;
      memo1.Clear;
      doc := CreateOleObject('Microsoft.XMLDOM') as IXMLDomDocument;
      // 加载country.xml文件
      doc.load('country.xml');
      nlist:=doc.getElementsByTagName('Records');
      memo1.lines.append('Table Name  :country');
      memo1.lines.append('---------------------');
      for i:=0 to nlist.Get_length-1 do
      begin
        travelChildren(nlist.Get_item(i).Get_childNodes);
        // 从dataRecord中滤掉第一个","
        strData:=copy(dataRecord,2,length(dataRecord));
        memo1.lines.append(strData);
        dataRecord:='';
        ret_val:=insertintotable(Datalist);
        if ret_val=1 then
          memo1.lines.append('Data inserted successfully.............!')
        else if ret_val=-1 then
          memo1.lines.append('Error while updating.....Try again.....!');
        memo1.lines.append('=============================================' + '==(Record no. :'+inttostr(count)+')');
        DataList.Clear;
        count:=count+1;
      end;
    except
      on e:Exception do
        Showmessage(e.message);
   end;
end;

nlist是节点列表。在本例中,第一个节点是:
<Records>
 <Name>Argentina</Name>
 <Capital>Buenos Aires</Capital>
 <Continent>South America</Continent>
 <Area>2777815</Area>
 <Population>32300003</Population>
</Records>

我们用travelchildren函数来解析节点。它用递归方式遍历节点列表,直到找到一个文本节点。这个文本数据被加入到TStringlist(Datalist)。第一次遍历完成后,可以在Datalist看到里看到Argentina, Buenos Aires, South America, 2777815, 32300003等字符串。然后把这个字符串传递给insertintotable函数,让它在country表中插入一条纪录。循环这个过程直至完成遍历整个XML文件。

procedure TForm1.travelChildren(nlist1:IXMLDOMNodeList);
var
  j:Integer;
  temp:String;
begin
  for j:=0 to nlist1.Get_length-1 do
  begin
  // 节点类型1表示是一个实体(entity),节点类型5表示EntityRef
  if((nlist1.Get_item(j).Get_nodeType= 1) or (nlist1.Get_item(j).Get_nodeType=5)) then
    travelChildren(nlist1.Get_item(j).Get_childNodes)
    // 节点类型3表示文本节点,即找到数据
    else if(nlist1.Get_item(j).Get_nodeType=3) then
    begin
      temp:= trim(nlist1.Get_item(j).Get_nodeValue);
      dataRecord:=dataRecord+','+temp;
      //在memo显示一条纪录
      DataList.Add(temp);
      // 完成一次节点扫描后包含一条纪录的信息list.
    end
  end;
end;

function TForm1.insertintotable(stpt:TStringList):Integer;
var
  I:Integer;
begin
  table1.close;
  table1.open;
  table1.Insert;
  for I := 0 to stpt.Count - 1 do
    table1.Fields[I].AsVariant:=stpt[I];
  try
    table1.post;
    result:=1;
  except
    on E:Exception do
      result:=-1;
  end;
end;

这个程序可以用于任何数据库,因此数据能以XML文件形式在网络(或者在互联网)上传递,实现远程更新。
如有问题,可与我联系。[/quote]

 
少爷 (2002-10-22 9:17:00)  

[center][red][h3]创建XML Datasets及动态控件[/h3][/red][/center]

[quote][blue][h4]本文介绍了如何创建XML dataset,然后通过XML文件里的元素对XML“表”动态地创建相应控件。[/h4][/blue][/quote]

[quote]本文介绍了如何创建XML dataset,然后通过XML文件里的元素对XML“表”动态地创建相应控件。文末所附的工程(Project)文件,使用户可以创建简单的(非关系型)数据库,这个数据库用XML文件存储。随后加载这个XML文件,经解析之后,可以在用户自行定义的“数据库”输入界面上动态生成控件。

大家都习惯使用Delphi来编程,实在太容易了。一桩困难的任务可以变得简单得多,伤透脑筋的结构设计可以轻易完成。感谢Delphi提供的这种灵活实用的工具,和优雅的界面风格。

Delphi 6问世,提供了新的组件,使开发者能够创建和管理XML文件。例如,在Interner组里提供了XMLDocument组件,在Data Access组里提供了XMLTransform, XMLTransformProvider, and XMLTransformClient组件集。

另外,在主菜单的File | New | Other | New下,你能找到XML数据绑定向导(XML Data Binding Wizard)。如果还不够,你可以使用XML映象器(Tools | XML Mapper or Programs | Borland Delphi 6 | XML Mapper)。尽管Delphi已经提供了如此之多的捷径来产生和使用XML文件,Borland公司还是向我们提供了“免动脑筋”的解决方案 - 使用“即试即用”的TClientDataSet。

从TClientDataSet到XML
用TClientDataSet创建和加载XML文件是很容易的,调用SaveToFile和LoadToFile函数即可,只要对函数的第二个参数设置相应的XML类型。实际上,有三个参数类型,一个是遗留的dfBinary二进制类型,另外是dfXMLUTF8和UTF8-based XML类型。

例如,可以这样存储文件(代码中的cds是TClientDataSet,FCurrentDataBaseName是文件名):
cds.SaveToFile(Format('%s.xml',    [FCurrentDataBaseName]), dfXMLUTF8);
这样你就得到了XML文件。

动态创建相应的控件
当然,在调用SaveToFile之前,还得创建数据库。这也不难,如下例。假设有两个TListBox控件,一个存放域名(lstbxFields),另一个存放相应的数据类型(lstbxDataTypes),动态创建控件的过程如下:

{----------------------------------------------------}
procedure TfEZDBBrowserAddEditDelete.Cre8Controls;
const  
 { DYNAMIC_CONTROLS_TOP:第一个控件与顶部的距离}  
 DYNAMIC_CONTROLS_TOP = 24;  
 DYNAMIC_CONTROLS_LEFT = 12;      
 { 为简短起见,省略其它常量}  
var  
 i: Integer;  
 iCurrentRow, iCurrentTop, iControlsOnThisRow,    iCurrentWidthOfControls: Integer;  
 MemoOnThisRow: Boolean;    

procedure Cre8StringLabeledEdit(ACaption: String);  
begin    
 with TLabeledEdit.Create(Self) do
 begin      
  Parent := scrlbx;      
  Name := Format('lbledt%s', [ACaption]);      
  { HelpContext用于映射控件和表格索引(grid index 3 = (control with HelpContext 3), 等等)}      
  HelpContext := HELP_CONTEXT_OFFSET+i;      
  Hint := ACaption;      
  EditLabel.Caption := ACaption;      
  EditLabel.Font.Style := EditLabel.Font.Style+[fsBold];      
  Text := '';      
  { 扩展宽度,使可输入长串}      
  Width := LABELED_EDIT_STRING_WIDTH;      
  grdcds.Columns[i].Width := GRID_COLUMN_ALPHA_WIDTH;      
  { 如果当前行宽不合适,去下一行}      
  if (iCurrentWidthOfControls + LABELED_EDIT_STRING_WIDTH +
   (SPACE_BETWEEN_DYNAMICALLY_CREATED_CONTROLS * iControlsOnThisRow)) >  
   MAX_WIDTH_OF_CONTROLS then
  begin
   inc(iCurrentRow);        
   iControlsOnThisRow := 1;        
   if MemoOnThisRow then          
    Top := (SPACE_BETWEEN_DYNAMICALLY_CRE8ED_ROWS * iCurrentRow)+MEMO_HEIGHT
   else
    Top := iCurrentTop+SPACE_BETWEEN_DYNAMICALLY_CRE8ED_ROWS;        
   iCurrentTop := Top;        
   MemoOnThisRow := False;        
   Left := DYNAMIC_CONTROLS_LEFT;        
   iCurrentWidthOfControls := LABELED_EDIT_STRING_WIDTH;      
  end
  else
  begin
   Top := iCurrentTop;        
   if iCurrentWidthOfControls = 0 then
   begin          
    Left := DYNAMIC_CONTROLS_LEFT;          
    if iCurrentRow=0 then            
     FFirstDynamicControl := Name;        
   end
   else          
    Left := iCurrentWidthOfControls + (SPACE_BETWEEN_DYNAMICALLY_CREATED_CONTROLS *
        iControlsOnThisRow) + DYNAMIC_CONTROLS_LEFT;        
   inc(iControlsOnThisRow);        
   iCurrentWidthOfControls := iCurrentWidthOfControls + LABELED_EDIT_STRING_WIDTH;      
  end;      
  OnEnter := DynamicControlEnter;      
  OnExit := DynamicControlExit;      
  Tag := LABELED_EDIT_STRING_TAG;    
 end;  
end;    

{ 创建Memos有一个特别问题:Memo很长时,要随时保留回车键("crlf")位置,用于调整屏幕顶端位置。}  

procedure Cre8Memo(ACaption: String);  
var     mmoLeft, mmoTop: Integer;  
begin    
 with TMemo.Create(Self) do
 begin      
  Parent := scrlbx;      
  Name := Format('mmo%s', [ACaption]);      
  HelpContext := HELP_CONTEXT_OFFSET+i;      
  Hint := ACaption;      
  Text := '';      
  Scrollbars := ssVertical;      
  { 如果当前行宽不合适,去下一行}      
  if (iCurrentWidthOfControls + MEMO_WIDTH + (SPACE_BETWEEN_DYNAMICALLY_CREATED_CONTROLS *  
   iControlsOnThisRow)) >  MAX_WIDTH_OF_CONTROLS then
  begin        
   inc(iCurrentRow);        
   iControlsOnThisRow := 1;        
   if MemoOnThisRow then          
    Top := iCurrentTop + SPACE_BETWEEN_DYNAMICALLY_CRE8ED_ROWS + MEMO_HEIGHT
   else
    Top := iCurrentTop + SPACE_BETWEEN_DYNAMICALLY_CRE8ED_ROWS;        
   iCurrentTop := Top;        
   Left := DYNAMIC_CONTROLS_LEFT;        
   iCurrentWidthOfControls := MEMO_WIDTH;      
  end
  else
  begin        
   Top := iCurrentTop;        
   if iCurrentWidthOfControls = 0 then
   begin          
    Left := DYNAMIC_CONTROLS_LEFT;          
    if iCurrentRow=0 then            
     FFirstDynamicControl := Name;        
   end
   else          
    Left := iCurrentWidthOfControls + (SPACE_BETWEEN_DYNAMICALLY_CREATED_CONTROLS *
       iControlsOnThisRow) + DYNAMIC_CONTROLS_LEFT;        
   inc(iControlsOnThisRow);        
   iCurrentWidthOfControls := iCurrentWidthOfControls + MEMO_WIDTH;      
  end;      
  { 现在可以在行例设置memo了}      
  MemoOnThisRow := True;      
  mmoLeft := Left;      
  mmoTop := Top;      
  OnEnter := DynamicControlEnter;      
  OnExit := DynamicControlExit;      
  Tag := MEMO_MEMO_TAG;    
 end;    
 { 在上面设置标签}    
 with TLabel.Create(Self) do
 begin      
  Parent := scrlbx;      
  { 也许可以不用Name... }      
  Name := Format('lbl%s', [ACaption]);      
  Caption := ACaption;      
  Font.Style := Font.Style+[fsBold];      
  Left := mmoLeft;      
  Top := mmoTop-LBL_VERTICAL_DELTA;    
 end;  
end;    

procedure Cre8MoneyLabeledEdit(ACaption: String);          
{为简短起见,这里省略代码,可到附件下载}    

procedure Cre8IntegerLabeledEdit(ACaption: String);          
{为简短起见,这里省略代码,可到附件下载}    

procedure Cre8FloatLabeledEdit(ACaption: String);          
{为简短起见,这里省略代码,可到附件下载}    

procedure Cre8ComboBox(ACaption: String);          
{为简短起见,这里省略代码,可到附件下载}    
begin   { 初始化变量}  
iCurrentRow := 0;  
iCurrentWidthOfControls := 0;  
iControlsOnThisRow := 0;  
iCurrentTop := DYNAMIC_CONTROLS_TOP;  
MemoOnThisRow := False;  
dm.cds.Active := True;  
{ Do not want to do this in the DM's Create, as the     CDS must be active first }  
dm.cds.LogChanges := False;  
for i := 0 to Pred(dm.CDS.FieldList.Count) do
begin    
 with dm.CDS.Fields do
  begin      
   if (Fields[i] is TStringField) then        
    Cre8StringLabeledEdit(Fields[i].FieldName)      
   else if (Fields[i] is TMemoField) then        
    Cre8Memo(Fields[i].FieldName)      
   else if (Fields[i] is TCurrencyField) then        
    Cre8MoneyLabeledEdit(Fields[i].FieldName)      
   else if (Fields[i] is TIntegerField) then        
    Cre8IntegerLabeledEdit(Fields[i].FieldName)      
   else if (Fields[i] is TFloatField) then        
    Cre8FloatLabeledEdit(Fields[i].FieldName)      
   else if (Fields[i] is TDateField) then        
    Cre8DateTimePicker(Fields[i].FieldName)      
   else if (Fields[i] is TBooleanField) then        
    Cre8ComboBox(Fields[i].FieldName);    
  end;  
 end;  
 Popul8DynamicControls;
end;  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值