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

吉祥小酒窝

吉祥小酒窝

2016-02-19 18:50

下面,图老师小编带您去了解一下用DELPHI的RTTI实现对象的XML持久化,生活就是不断的发现新事物,get新技能~
去年我花了很多时间尝试用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跑一遍,非常的麻烦。

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

      所以当我想到数据集的对象化后,立即想到也可以用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持久化。

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

      三是一般的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表中这几个字段的内容将被复制到此表中。

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/bianchengyuyan/)
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了--虽然有好用的工具,但能省点事终归是好的。

展开更多 50%)
分享

猜你喜欢

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

编程语言 网络编程
用DELPHI的RTTI实现对象的XML持久化

用DELPHI的RTTI实现数据集的简单对象化

编程语言 网络编程
用DELPHI的RTTI实现数据集的简单对象化

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

用Delphi实现文件关联

Delphi
用Delphi实现文件关联

通过序列化和反序列化泛型数据实体集合来实现持久化数据对象的方

Web开发
通过序列化和反序列化泛型数据实体集合来实现持久化数据对象的方

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

用Delphi实现壁纸更换

编程语言 网络编程
用Delphi实现壁纸更换

用Delphi实现打印功能

Delphi
用Delphi实现打印功能

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

自动生成拼音(汉字反查到拼音)

自动生成拼音(汉字反查到拼音)

AJAX技术可能会扩大安全方面的威胁

AJAX技术可能会扩大安全方面的威胁
下拉加载更多内容 ↓