去年我花了很多时间尝试用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实现数据集的简单对象化》中已经实现的部分,做了一个简单的实现:

  1. TMXMLPersistent = class(TObject)
  2. public
  3. class Procedure LoadObjFromXML( aNode : IXMLNode; aObj : TPersistent );
  4. class Procedure SaveObjToXML( aNode : IXMLNode; aObj : TPersistent );
  5. end;
  6.  
  7. const
  8. DefaultFilter : TTypeKinds = [tkInteger, tkChar, tkEnumeration,
  9. tkFloat, tkString, tkSet, tkWChar, tkLString, tkWString, tkInt64];
  10.  
  11. { TMXMLPersistent }
  12.  
  13. class procedure TMXMLPersistent.LoadObjFromXML(aNode: IXMLNode;
  14. aObj: TPersistent);
  15. Var
  16. i : Integer;
  17. pList : TMPropList;
  18. pInfo : PPropInfo;
  19. tmpObj: TObject;
  20. begin
  21. If ( aObj Is TMDataSetProxy ) Then
  22. ( aObj As TMDataSetProxy ).LoadFromXML( aNode )
  23. Else
  24. Begin
  25. pList := TMPropList.Create( aObj );
  26. Try
  27. For i := 0 To pList.PropCount - 1 Do
  28. Begin
  29. pInfo := pList.Props[i];
  30. If ( pInfo^.PropType^.Kind = tkClass ) Then
  31. Begin
  32. tmpObj := TObject( Integer( GetPropValue( aObj, pInfo^.Name ) ) );
  33. If ( Assigned( tmpObj ) AND ( tmpObj Is TPersistent ) ) Then
  34. LoadObjFromXML( aNode.ChildNodes[WideString(pInfo^.Name)],
  35. tmpObj As TPersistent );
  36. End
  37. Else If ( pInfo^.PropType^.Kind In DefaultFilter ) Then
  38. SetPropValue( aObj, pInfo^.Name,
  39. String( aNode.ChildNodes[WideString( pInfo^.Name )].Text ) );
  40. End;
  41. Finally
  42. pList.Free;
  43. End;
  44. End;
  45. end;
  46.  
  47. class procedure TMXMLPersistent.SaveObjToXML(aNode: IXMLNode;
  48. aObj: TPersistent);
  49. Var
  50. i : Integer;
  51. pList : TMPropList;
  52. pInfo : PPropInfo;
  53. tmpObj: TObject;
  54. begin
  55. If ( aObj Is TMDataSetProxy ) Then
  56. ( aObj As TMDataSetProxy ).SaveToXML( aNode )
  57. Else
  58. Begin
  59. pList := TMPropList.Create( aObj );
  60. Try
  61. For i := 0 To pList.PropCount - 1 Do
  62. Begin
  63. pInfo := pList.Props[i];
  64. If ( pInfo^.PropType^.Kind = tkClass ) Then
  65. Begin
  66. tmpObj := TObject( Integer( GetPropValue( aObj, pInfo^.Name ) ) );
  67. If ( Assigned( tmpObj ) AND ( tmpObj Is TPersistent ) ) Then
  68. SaveObjToXML( aNode.AddChild( WideString( pInfo^.Name ) ),
  69. tmpObj As TPersistent );
  70. End
  71. Else If ( pInfo^.PropType^.Kind In DefaultFilter ) Then
  72. aNode.AddChild( WideString( pInfo^.Name ) ).Text :=
  73. GetPropValue( aObj, pInfo^.Name );
  74. End;
  75. Finally
  76. pList.Free;
  77. End;
  78. End;
  79. 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时会方便很多。

  1. procedure TMDataSetProxy.LoadFromXML(aNode: IXMLNode);
  2. Var
  3. i, j : Integer;
  4. pInfo : PPropInfo;
  5. pRow : IXMLNode;
  6. begin
  7. For j := 0 To aNode.ChildNodes.Count - 1 Do
  8. Begin
  9. FDataSet.Append;
  10. pRow := aNode.ChildNodes[j];
  11. For i := 0 To FPropList.PropCount - 1 Do
  12. Begin
  13. pInfo := FPropList.Props[i];
  14. If ( pInfo^.PropType^.Kind In DefaultFilter ) Then
  15. SetVariant( i,
  16. String( pRow.ChildNodes[WideString( pInfo^.Name )].Text ) );
  17. End;
  18. EndEdit;
  19. End;
  20. FDataSet.First;
  21. end;
  22.  
  23. procedure TMDataSetProxy.SaveToXML(aNode: IXMLNode);
  24. Var
  25. i : Integer;
  26. pInfo : PPropInfo;
  27. pRow : IXMLNode;
  28. begin
  29. While ForEach Do
  30. Begin
  31. pRow := aNode.AddChild( 'Row' );
  32. For i := 0 To FPropList.PropCount - 1 Do
  33. Begin
  34. pInfo := FPropList.Props[i];
  35. If ( pInfo^.PropType^.Kind In DefaultFilter ) Then
  36. pRow.AddChild( WideString( pInfo^.Name ) ).Text
  37. := GetVariant( i );
  38. End;
  39. End;
  40. end;

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

  1. TDemoCompany = class( TPersistent )
  2. private
  3. FEmployee : TDSPEmployee;
  4. FCompany : String;
  5. FCode : Integer;
  6. published
  7. property Employee : TDSPEmployee Read FEmployee Write FEmployee;
  8. property Company : String Read FCompany Write FCompany;
  9. Property Code : Integer Read FCode Write FCode;
  10. End;
  11.  
  12. procedure TForm1.SaveClick(Sender: TObject);
  13. Var
  14. demo : TDemoCompany;
  15. begin
  16. demo := TDemoCompany.Create;
  17. demo.Employee := TDSPEmployee.Create( ADODataSet1 );
  18. demo.Company := 'Demo company';
  19. demo.Code := 987654;
  20. Try
  21. XMLDocument1.Active := true;
  22. TMXMLPersistent.SaveObjToXML( XMLDocument1.AddChild( 'Demo' ), demo );
  23. XMLDocument1.SaveToFile( 'temp.xml' );
  24. XMLDocument1.Active := false;
  25. Finally
  26. demo.Employee.Free;
  27. demo.Employee := Nil;
  28. demo.Free;
  29. End;
  30. end;
  31.  
  32. procedure TForm1.LoadClick(Sender: TObject);
  33. Var
  34. demo : TDemoCompany;
  35. begin
  36. demo := TDemoCompany.Create;
  37. demo.Employee := TDSPEmployee.Create( ADODataSet2 );
  38. Try
  39. XMLDocument1.Active := true;
  40. XMLDocument1.LoadFromFile( 'temp.xml' );
  41. TMXMLPersistent.LoadObjFromXML( XMLDocument1.ChildNodes.Last, demo );
  42. XMLDocument1.Active := false;
  43. Edit1.Text := demo.Company;
  44. Edit2.Text := IntToStr( demo.Code );
  45. While ( demo.Employee.ForEach ) Do
  46. With ListView1.Items.Add Do
  47. Begin
  48. Caption := IntToStr( demo.Employee.EmployeeID );
  49. SubItems.Add( demo.Employee.FirstName );
  50. SubItems.Add( demo.Employee.LastName );
  51. SubItems.Add( FormatDateTime( 'yyyy-mm-dd', demo.Employee.BirthDate ) );
  52. End;
  53. Finally
  54. demo.Employee.Free;
  55. demo.Employee := Nil;
  56. demo.Free;
  57. End;
  58. end;

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

http://blog.csdn.net/diligentcatrich/article/details/6934109

用DELPHI的RTTI实现对象的XML持久化的更多相关文章

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

    在<强大的DELPHI RTTI--兼谈需要了解多种开发语言>一文中,我说了一下我用DELPHI的RTTI实现了数据集的简单对象化.本文将详细介绍一下我的实现方法.     首先从一个简单 ...

  2. Delphi 的RTTI机制浅探3(超长,很不错)

    转自:http://blog.sina.com.cn/s/blog_53d1e9210100uke4.html 目录========================================== ...

  3. 通过JAXB完成Java对象与XML之间的转换

    Java对象转换XML的过程叫marshal. XML转换到Java对象的过程叫unmarshal. 一.Java对象转化为XML 这里省略getter和setter方法 通过标注@XMLRootEl ...

  4. java对象与XML相互转化

    起因 最近在公司做了一次webservice相关的任务,其中我最敢兴趣的就是webservice接受到XML对应的流以后是如何方便的转化成java对象,而java对象又是如何生成对应的XML的. 目的 ...

  5. XmlSerializer 对象的Xml序列化和反序列化

    http://www.cnblogs.com/yukaizhao/archive/2011/07/22/xml-serialization.html 这篇随笔对应的.Net命名空间是System.Xm ...

  6. 序列化对象为xml字符串

    /// <summary>    /// 序列化对象为xml字符串    /// </summary>    /// <param name="obj" ...

  7. Java对象表示方式2:XStream实现对对象的XML化

    上一篇文章讲到了使用Java原生的序列化的方式来表示一个对象.总结一下这种对象表示方式的优缺点: 1.纯粹的Java环境下这种方式可以很好地工作,因为它是Java自带的,也不需要第三方的Jar包的支持 ...

  8. JAVA对象和XML文档、原来他们之间还有这一出

    最近项目开发中遇到一个问题,访问接口不再通过url地址请求的方式,而是 通过socket发送xml格式的报文到指定服务器来进行信息的统一认证.. 因此组装xml格式的报文字符串以及解析服务器返回的xm ...

  9. 玩转Java对象和XML相互转换

    最近在项目中一直出现Java对象和XML之间的相互转换,一开始由于项目很庞大,我又是临时调度过去,导致在按照项目组长的要求进行写代码的同时,总是在这块云里雾里,最近才慢慢开始搞清楚项目中具体的使用缘由 ...

随机推荐

  1. 面向对象程序设计-C++ Steam & Vector 【第三次上课笔记】

    大家可以下载后用Vim 或者 Sublime Text等文本编辑器查看 Conference: http://blog.csdn.net/candy1232009/article/details/70 ...

  2. Week11(11月18日)

    Part I:检查 =========================== 1.上堂课的练习效果. Part II:案例学习 =========================== MusicStor ...

  3. xcode生成的IOS安装文件的位置

    通过xcode生成可以在IOS系统下运行的文件的具体设置: 1.首先,需要有相应的程序,并且在mac下的xcode编译后,能够在模拟器中完美运行. 2.单击xcode,打开Xcode > Pre ...

  4. solrCloud+tomcat+zookeeper配置

    一.环境准备: Solr版本:4.7.0 下载地址:http://www.apache.org/dyn/closer.cgi/lucene/solr/4.7.0 Tomcat版本:6.0.39 下载地 ...

  5. C# MVC 自学笔记—4 添加视图

    ==============================翻译============================== 在本节中,你将要修改 HelloWorldController 类,以便使 ...

  6. 基于visual Studio2013解决算法导论之016查找最大值最小值

     题目 查找最大.最小值 解决代码及点评 #include <stdio.h> #include <stdlib.h> #include <malloc.h> ...

  7. 深入浅出Win32多线程设计之MFC的多线程-线程与消息队列(经典)

    1.创建和终止线程 在MFC程序中创建一个线程,宜调用AfxBeginThread函数.该函数因参数不同而具有两种重载版本,分别对应工作者线程和用户接口(UI)线程. 工作者线程 CWinThread ...

  8. SystemTap----将SystemTap脚本编译成内核模块

      当运行SystemTap脚本时,会根据脚本生成一个内核模块,然后插入到系统中执行后退出.这个过程总共分为5个阶段:parse, elaborate, translate, compile, run ...

  9. CAN总线基础

    can总线协议: 涵盖了OSI规定的传输层.数据链路层.物理层 物理层: 决定了位编码方式(NRZ编码,6个位插入填充位),位时序(位时序.位的采样).同步方式(根据同步段ss实现同步,并具有再同步功 ...

  10. IOS SWIFT 网络请求JSON解析 基础一

    前言:移动互联网时代,网络通信已经是手机端必不可少的功能.应用中也必不可少地使用了网络通信,增强客户端与服务器交互.使用NSURLConnection实现HTTP的通信.NSURLConnection ...