用DELPHI的RTTI实现对象的XML持久化
2008-04-09 04:22:02来源:互联网 阅读 ()
去年我花了很多时间尝试用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) public class Procedure LoadObjFromXML( aNode : IXMLNode; aObj : TPersistent ); class Procedure SaveObjToXML( aNode : IXMLNode; aObj : TPersistent ); end; const DefaultFilter : TTypeKinds = [tkInteger, tkChar, tkEnumeration, tkFloat, tkString, tkSet, tkWChar, tkLString, tkWString, tkInt64]; { TMXMLPersistent } class procedure TMXMLPersistent.LoadObjFromXML(aNode: IXMLNode; aObj: TPersistent); Var i : Integer; pList : TMPropList; pInfo : PPropInfo; tmpObj: TObject; begin If ( aObj Is TMDataSetProxy ) Then ( aObj As TMDataSetProxy ).LoadFromXML( aNode ) Else Begin pList := TMPropList.Create( aObj ); Try For i := 0 To pList.PropCount - 1 Do Begin 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; Finally pList.Free; End; End; end; class procedure TMXMLPersistent.SaveObjToXML(aNode: IXMLNode; aObj: TPersistent); Var i : Integer; pList : TMPropList; pInfo : PPropInfo; tmpObj: TObject; begin If ( aObj Is TMDataSetProxy ) Then ( aObj As TMDataSetProxy ).SaveToXML( aNode ) Else Begin pList := TMPropList.Create( aObj ); Try For i := 0 To pList.PropCount - 1 Do Begin 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; Finally pList.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); Var i, j : Integer; pInfo : PPropInfo; pRow : IXMLNode; begin For j := 0 To aNode.ChildNodes.Count - 1 Do Begin FDataSet.Append; pRow := aNode.ChildNodes[j]; For i := 0 To FPropList.PropCount - 1 Do Begin pInfo := 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); Var i : Integer; pInfo : PPropInfo; pRow : IXMLNode; begin While ForEach Do Begin pRow := aNode.AddChild( ''''Row'''' ); For i := 0 To FPropList.PropCount - 1 Do Begin pInfo := FPropList.Props[i]; If ( pInfo^.PropType^.Kind In DefaultFilter ) Then pRow.AddChild( WideString( pInfo^.Name ) ).Text := GetVariant( i ); End; End; end;标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash