用DELPHI的RTTI实现对象的XML持久化
去年我花了很多时间尝试用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;
下面是一个简单的DEMO,其中包括了对数据集的XML持久化。注意Load的时候Employee成员连接的是ADODataSet2,它连接到一个包含了这几个字段的表,各字段类型与Employee表相同,但内容为空,并且去掉了EmployeeID的Identity。Load完成后,Employee表中这几个字段的内容将被复制到此表中。
TDemoCompany = class( TPersistent )
private
FEmployee : TDSPEmployee;
FCompany : String;
FCode : Integer;
published
property 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);
Var
demo : TDemoCompany;
begin
demo := TDemoCompany.Create;
demo.Employee := TDSPEmployee.Create( ADODataSet1 );
demo.Company := 'Demo company';
demo.Code := 987654;
Try
XMLDocument1.Active := true;
TMXMLPersistent.SaveObjToXML( XMLDocument1.AddChild( 'Demo' ), demo );
XMLDocument1.SaveToFile( 'temp.xml' );
XMLDocument1.Active := false;
Finally
demo.Employee.Free;
demo.Employee := Nil;
demo.Free;
End;
end; procedure TForm1.LoadClick(Sender: TObject);
Var
demo : TDemoCompany;
begin
demo := TDemoCompany.Create;
demo.Employee := TDSPEmployee.Create( ADODataSet2 );
Try
XMLDocument1.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 ) Do
With ListView1.Items.Add Do
Begin
Caption := IntToStr( demo.Employee.EmployeeID );
SubItems.Add( demo.Employee.FirstName );
SubItems.Add( demo.Employee.LastName );
SubItems.Add( FormatDateTime( 'yyyy-mm-dd', demo.Employee.BirthDate ) );
End;
Finally
demo.Employee.Free;
demo.Employee := Nil;
demo.Free;
End;
end;
终于可以告别那个麻烦的XML Data binding了,并且以后也不用写XSD了--虽然有好用的工具,但能省点事终归是好的。
http://blog.csdn.net/diligentcatrich/article/details/6934109
用DELPHI的RTTI实现对象的XML持久化的更多相关文章
- 用DELPHI的RTTI实现数据集的简单对象化
在<强大的DELPHI RTTI--兼谈需要了解多种开发语言>一文中,我说了一下我用DELPHI的RTTI实现了数据集的简单对象化.本文将详细介绍一下我的实现方法. 首先从一个简单 ...
- Delphi 的RTTI机制浅探3(超长,很不错)
转自:http://blog.sina.com.cn/s/blog_53d1e9210100uke4.html 目录========================================== ...
- 通过JAXB完成Java对象与XML之间的转换
Java对象转换XML的过程叫marshal. XML转换到Java对象的过程叫unmarshal. 一.Java对象转化为XML 这里省略getter和setter方法 通过标注@XMLRootEl ...
- java对象与XML相互转化
起因 最近在公司做了一次webservice相关的任务,其中我最敢兴趣的就是webservice接受到XML对应的流以后是如何方便的转化成java对象,而java对象又是如何生成对应的XML的. 目的 ...
- XmlSerializer 对象的Xml序列化和反序列化
http://www.cnblogs.com/yukaizhao/archive/2011/07/22/xml-serialization.html 这篇随笔对应的.Net命名空间是System.Xm ...
- 序列化对象为xml字符串
/// <summary> /// 序列化对象为xml字符串 /// </summary> /// <param name="obj" ...
- Java对象表示方式2:XStream实现对对象的XML化
上一篇文章讲到了使用Java原生的序列化的方式来表示一个对象.总结一下这种对象表示方式的优缺点: 1.纯粹的Java环境下这种方式可以很好地工作,因为它是Java自带的,也不需要第三方的Jar包的支持 ...
- JAVA对象和XML文档、原来他们之间还有这一出
最近项目开发中遇到一个问题,访问接口不再通过url地址请求的方式,而是 通过socket发送xml格式的报文到指定服务器来进行信息的统一认证.. 因此组装xml格式的报文字符串以及解析服务器返回的xm ...
- 玩转Java对象和XML相互转换
最近在项目中一直出现Java对象和XML之间的相互转换,一开始由于项目很庞大,我又是临时调度过去,导致在按照项目组长的要求进行写代码的同时,总是在这块云里雾里,最近才慢慢开始搞清楚项目中具体的使用缘由 ...
随机推荐
- Android中弹出对话框,AlertDialog关键代码
写在这里便于以后查看. Android中弹出对话框的关键代码: btn01.setOnClickListener(new OnClickListener() { @Override public vo ...
- 【转】Shell编程
原文链接: Shell编程 打算有时间简单了解shell编程 1.shell结构 一个简单的例子: [root@localhost shell]# vi example #!/bin/sh #Thi ...
- CodeForces 225C Barcode DP
也是一道dp ,想到了就会觉得很巧妙 矩阵中只有白块和黑块,要求repaint后满足下述条件: 每列一种颜色 根据输入范围x, y 要求条纹宽度在[x, y] 之间 数据范围: n, m, x and ...
- LOVEU
闲来无事,自己编写一个小程序,自娱自乐 //date: 2013/8/14 //designer :pengxiaoen //function : printf the word love #incl ...
- 基于visual Studio2013解决C语言竞赛题之0418位数操作
题目 解决代码及点评 /************************************************************************/ /* 18. 给 ...
- 深入探究VC —— 链接器link.exe(4)
在程序编译完成后,生成的文件是以.obj为扩展名的对象文件,link.exe是将这些对象文件与库链接起来以创建可执行文件或动态链接库文件的工具. link.exe的输入文件包括obj文件.lib文件. ...
- The connection to adb is down, and a severe error has occured.问题解决
遇到问题描述: 运行android程序控制台输出 [2013-06-25 11:10:32 - MyWellnessTracker] The connection to adb is down, an ...
- 翻转句子中单词的顺序 C语言
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变.句子中单词以空格符隔开. 为简单起见,标点符号和普通字母一样处理. 比如将"I am a student"转化为&q ...
- 2014 International Conference on Robotics and Computer Vision (ICRVC 2014)
2014机器人与计算机视觉国际会议ICRVC 与会地点:北京 与会时间:2014.10.24-26 截稿日期:2014-07-10 关于征稿: 语言:英文 主题: • Evolutionary Rob ...
- 工具类CTools实现字符编码转换和获取当前路径
class CTools { public: CTools(void); public: ~CTools(void); public: static std::string UNICODE_to_UT ...