用DELPHI的RTTI实现对象的XML持久化

2016-02-19 18:50 59 1 收藏

下面,图老师小编带您去了解一下用DELPHI的RTTI实现对象的XML持久化,生活就是不断的发现新事物,get新技能~

【 tulaoshi.com - 编程语言 】

去年我花了很多时间尝试用DELPHI进行基于XML的WEB应用开发。起初的设想是很美好的,但结果做出来的东西很简陋。一部分原因就在于XML到Object之间的数据绑定实现太麻烦(另一部分是因为对XSLT不熟,学习它花了很多时间)。

      之前我一直是用DELPHI提供的XML Data binding来做的,基本做法是:先用工具(如XMLSPY)做好一个XML Schema(XSD),然后用XML Data binding生成DELPHI的接口和类。当然,一旦生成好就很方便了,在程序里我只要操作这个接口就好了,其中各个Field都会被变成属性,并且类型也都如我在XSD中的定义。但问题在于程序在开发过程中,总是会有一些变化的,在这种情况下,我就不得不同时开着XMLSPY修改XSD,然后重新用 XML Data binding的Wizard跑一遍,非常的麻烦。

      所以当我想到数据集的对象化后,立即想到也可以用RTTI来实现Object的XML持久化--其实DELPHI6开始的SOAP实现就是用RTTI来实现Object到SOAP数据(就是XML)的转换的。显然我已经是非常的后知后觉了,当我在《强大的DELPHI RTTI--兼谈需要了解多种开发语言》一文中说到我的打算时,朋友Lex CHow回复我说他在大约一年前就做过了这方面的工作,我当即跟他要来了他的源码。lexlib是他写的是一个有很多功能的库,看上去结构有点像.net 的基本类库(当然没那么大^O^),Object的XML持久化只是其中的很小的一部分。因为我只需要这一部分,就没必要用这整个一个库这么麻烦,于是参考了lexlib并结合我在《用DELPHI的RTTI实现数据集的简单对象化》中已经实现的部分,做了一个简单的实现:

TMXMLPersistent = class(TObject)publicclass Procedure LoadObjFromXML( aNode : IXMLNode; aObj : TPersistent );class Procedure SaveObjToXML(   aNode : IXMLNode; aObj : TPersistent );end;constDefaultFilter : TTypeKinds = [tkInteger, tkChar, tkEnumeration,tkFloat, tkString, tkSet, tkWChar, tkLString, tkWString, tkInt64];{ TMXMLPersistent }class procedure TMXMLPersistent.LoadObjFromXML(aNode: IXMLNode;  aObj: TPersistent);Vari : Integer;pList : TMPropList;pInfo : PPropInfo;tmpObj: TObject;beginIf ( aObj Is TMDataSetProxy ) Then( aObj As TMDataSetProxy ).LoadFromXML( aNode )ElseBeginpList := TMPropList.Create( aObj );TryFor i := 0 To pList.PropCount - 1 DoBegin  pInfo := pList.Props[i];  If ( pInfo^.PropType^.Kind = tkClass ) Then  Begin  tmpObj := TObject( Integer( GetPropValue( aObj, pInfo^.Name ) ) );  If ( Assigned( tmpObj ) AND ( tmpObj Is TPersistent ) ) Then  LoadObjFromXML( aNode.ChildNodes[WideString(pInfo^.Name)], tmpObj As TPersistent );  End  Else If ( pInfo^.PropType^.Kind In DefaultFilter ) Then  SetPropValue( aObj, pInfo^.Name,  String( aNode.ChildNodes[WideString( pInfo^.Name )].Text ) );End;FinallypList.Free;End;End;end;class procedure TMXMLPersistent.SaveObjToXML(aNode: IXMLNode;  aObj: TPersistent);Vari : Integer;pList : TMPropList;pInfo : PPropInfo;tmpObj: TObject;beginIf ( aObj Is TMDataSetProxy ) Then( aObj As TMDataSetProxy ).SaveToXML( aNode )ElseBeginpList := TMPropList.Create( aObj );TryFor i := 0 To pList.PropCount - 1 DoBegin  pInfo := pList.Props[i];  If ( pInfo^.PropType^.Kind = tkClass ) Then  Begin  tmpObj := TObject( Integer( GetPropValue( aObj, pInfo^.Name ) ) );  If ( Assigned( tmpObj ) AND ( tmpObj Is TPersistent ) ) Then  SaveObjToXML( aNode.AddChild( WideString( pInfo^.Name ) ), tmpObj As TPersistent );  End  Else If ( pInfo^.PropType^.Kind In DefaultFilter ) Then  aNode.AddChild( WideString( pInfo^.Name ) ).Text :=  GetPropValue( aObj, pInfo^.Name );End;FinallypList.Free;End;End;end;

      这个实现应该说是很简单的。主要是三个部分(Load和Save的结构是相似的):

      一是对TMDataSetProxy作特别处理,委托给这个类自己去处理它的实现,因为它与一般的类不同,需要通过ForEach遍历全部记录,这其实就是同时实现数据集的XML持久化。

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

      二是对Class作递归处理,当然只支持从TPersistent派生的class。

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

      三是一般的Field简单地转成String保存,其中借鉴了lexlib的Filter,只处理那些能简单地转成String的数据类型,过滤掉那些可能造成转换出错的类型。

      上面的代码中用到的TMPropList见《用DELPHI的RTTI实现数据集的简单对象化》中的实现。
  

      下面是用TMDataSetProxy实现的数据集的XML持久化。免去了需要通过TClientDataSet进行的麻烦,并且采用的是用Node记录字段的方式,.net也是采用这样的方式,与TClientDataSet所用的用Attribute记录字段的方式不同。虽然这样生成的 XML文件将会略大一些,但是好处也是显而易见的,特别是我是准备用在Web应用中的,用Node方式记录的XML,在用XSLT时会方便很多。

procedure TMDataSetProxy.LoadFromXML(aNode: IXMLNode);Vari, j : Integer;pInfo : PPropInfo;pRow  : IXMLNode;beginFor j := 0 To aNode.ChildNodes.Count - 1 DoBeginFDataSet.Append;pRow := aNode.ChildNodes[j];For i := 0 To FPropList.PropCount - 1 DoBeginpInfo := FPropList.Props[i];If ( pInfo^.PropType^.Kind In DefaultFilter ) Then  SetVariant( i,  String( pRow.ChildNodes[WideString( pInfo^.Name )].Text ) );End;EndEdit;End;FDataSet.First;end;procedure TMDataSetProxy.SaveToXML(aNode: IXMLNode);Vari : Integer;pInfo : PPropInfo;pRow  : IXMLNode;beginWhile ForEach DoBeginpRow := aNode.AddChild( 'Row' );For i := 0 To FPropList.PropCount - 1 DoBeginpInfo := FPropList.Props[i];If ( pInfo^.PropType^.Kind In DefaultFilter ) Then  pRow.AddChild( WideString( pInfo^.Name ) ).Text  := GetVariant( i );End;End;end;

      下面是一个简单的DEMO,其中包括了对数据集的XML持久化。注意Load的时候Employee成员连接的是ADODataSet2,它连接到一个包含了这几个字段的表,各字段类型与Employee表相同,但内容为空,并且去掉了EmployeeID的Identity。Load完成后,Employee表中这几个字段的内容将被复制到此表中。

TDemoCompany = class( TPersistent )privateFEmployee : TDSPEmployee;FCompany  : String;FCode : Integer;publishedproperty Employee : TDSPEmployee Read FEmployee Write FEmployee;property Company  : String   Read FCompany  Write FCompany;Property Code : Integer  Read FCode Write FCode;End;procedure TForm1.SaveClick(Sender: TObject);Vardemo : TDemoCompany;begindemo := TDemoCompany.Create;demo.Employee := TDSPEmployee.Create( ADODataSet1 );demo.Company  := 'Demo company';demo.Code := 987654;TryXMLDocument1.Active := true;TMXMLPersistent.SaveObjToXML( XMLDocument1.AddChild( 'Demo' ), demo );XMLDocument1.SaveToFile( 'temp.xml' );XMLDocument1.Active := false;Finallydemo.Employee.Free;demo.Employee := Nil;demo.Free;End;end;procedure TForm1.LoadClick(Sender: TObject);Vardemo : TDemoCompany;begindemo := TDemoCompany.Create;demo.Employee := TDSPEmployee.Create( ADODataSet2 );TryXMLDocument1.Active := true;XMLDocument1.LoadFromFile( 'temp.xml' );TMXMLPersistent.LoadObjFromXML( XMLDocument1.ChildNodes.Last, demo );XMLDocument1.Active := false;Edit1.Text := demo.Company;Edit2.Text := IntToStr( demo.Code );While ( demo.Employee.ForEach ) DoWith ListView1.Items.Add DoBeginCaption := IntToStr( demo.Employee.EmployeeID );SubItems.Add( demo.Employee.FirstName );SubItems.Add( demo.Employee.LastName );SubItems.Add( FormatDateTime( 'yyyy-mm-dd', demo.Employee.BirthDate ) );End;Finallydemo.Employee.Free;demo.Employee := Nil;demo.Free;End;end;

      终于可以告别那个麻烦的XML Data binding了,并且以后也不用写XSD了--虽然有好用的工具,但能省点事终归是好的。

来源:https://www.tulaoshi.com/n/20160219/1619123.html

延伸阅读
对一些需要将数据持久化的小型程序中,传统的关系型数据库显得庞大而不实用,OO数据库有一个学习曲线的问题,而使用XML是一种较好的选择.本文将就设计一个合理的XML持久化的解决方案进行探讨. 使用XML作为持久层解决方案的,它的基本功能要有: 1.对象的CRUD功能(本例中基本对象是Member类). 2.保证线程安全,对桌面程序和Web程序...
标签: Delphi
如果我们把Handle指向桌面,哪TMediaPlay将会在桌面播放。与TCanvas不同的是,TWinControl的Handle的属性是只读的,并且GetHandle方法是静态方法,这说明,我们要Handle指向桌面只有两种方法 修改TWinControl,让Handle可写。 建立TWinControl子类,并隐藏TWinControl.GetHandle方法,在新方法中返回桌面句柄。 其中第二...
用DELPHI实现文件加密压缩 作者: e梦缘 (wnhoo) Mail:wnhoo@163.com 风花雪月 e梦情缘 点击下载此详细说明文件 概述: 在这篇文件中,讲述对单个文件的数据加密、数据压缩、自解压的实现。同样,也可以实现对多个文件或文件夹的压缩,只要稍加修改便可实现。 关键...
标签: Web开发
    微软已经意识到串行化数据的重要性,因此在.NET框架中包含了命名空间System.Runtime.Serialization和System.Xml.Serialization以提供串行化功能,为用户自己编写串行化方法提供了一个框架。System.Xml.Serialization命名空间提供了将一个对象串行化为XML格式的基本方法。下面我们来看看如何使用这种方法。 XML的魅...
标签: Delphi
  在网络管理中,有时需要通过监视远程计算机屏幕来了解网上微机的使用情况。虽然,市面上有很多软件可以实现该功能,有些甚至可以进行远程控制,但在使用上缺乏灵活性,如无法指定远程计算机屏幕区域的大小和位置,进而无法在一屏上同时监视多个屏幕。其实,可以用Delphi自行编制一个灵活的远程屏幕抓取工具,简述如下。 一...

经验教程

145

收藏

36
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部