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了--虽然有好用的工具,但能省点事终归是好的。

时间: 2024-11-01 19:07:20

Rtti实现对象的XML持久化的相关文章

使用Quick在Java对象和XML之间进行转换

近几年来,XML 的确给编程世界带来了巨大冲击.然而,XML 应用程序的复杂 性(从一开始就很复杂)在最近几年中并没有减少多少.开发人员仍要花几个星 期的时间(即使不是几个月)来学习复杂的 XML 语义和 API(如 SAX 和 DOM) 来操作 XML.然而,在过去的 6 个月到 12 个月中,相对于那些较复杂的 API, 另一种新的比较简单的 XML API(称为 Quick)已经越来越受到欢迎. 数据绑定允许您直接在 Java 对象和 XML 之间映射,而不必处理 XML 属性和 元素.另

XML的操作——JAXB进行Java对象和XML之间的转换

JAXB(Java Architecture for XML Binding)是一种特殊的序列化/反序列化工具,可实现Java对象与XML的相互转换. 在JAXB中将一个Java对象-->XML的过程称之为Marshal,XML-->Java对象的过程称之为UnMarshal. @XmlRootElement public class SClass {private String cnum;private List<Student> students;public SClass()

.NET对象的XML序列化和反序列化

 序列化的概念 序列化是指一个对象的实例可以被保存,保存成一个二进制串,当然,一旦被保存成二进制串,那么也可以保存成文本串了.比如,一个计数器,数值为2,我们可以用字符串"2"表示.如果有个对象,叫做connter,当前值为2,那么可以序列化成"2",反向的,也可以从"2"得到值为2的计数器实例.这样,关机时序列化它,开机时反序列化它,每次开机都是延续的.不会都是从头开始. 序列化概念的提出和实现,可以使我们的应用程序的设置信息保存和读取更加方便

JAVA对象和XML文件绑定

目的     在日常工作中,我们经常处理这些编组关系:JAVA对象 <--> XML文件:JAVA对象 <--> 数据库表.本文简单介绍一下JAVA对象和XML文件之间的绑定.虽然代码简单,但解决问题的广度与思路也是非常重要的.     首先,在Maven中引入要用到的jar包. <dependency> <groupId>castor</groupId> <artifactId>castor</artifactId> &

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

try { XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("my", "http://flibble"); Workers workers = new Workers(); workers.MyWorkers = new Worker[] { new Worker() { Name = "1", Number = 1 }, new Worker() { Nam

详解PHP原生DOM对象操作XML的方法_php技巧

一.创建 创建一个新的XML文件,并且写入一些数据到这个XML文件中. /* * 创建xml文件 */ $info = array( array('obj' => 'power','info' => 'power is shutdown'), array('obj' => 'memcache','info' => 'memcache used than 90%'), array('obj' => 'cpu','info' => 'cpu used than 95%'),

asp.net 自定义对象的Xml序列化

asp教程.net 自定义对象的Xml序列化 System.Xml.Serialization命名空间中有一系列的特性类,用来控制复杂类型序列化的控制.例如XmlElementAttribute.XmlAttributeAttribute.XmlArrayAttribute.XmlArrayItemAttribute.XmlRootAttribute等等. 看一个小例子,有一个自定义类Cat,Cat类有三个属性分别为Color,Saying,Speed. namespace UseXmlSeri

AJAX对象调用(XML HTTP Request)

ajax|request|xml|对象 作者:Jim Ley(主页) 译者:Sheneyan(子乌)(主页) 时间:2006.1.29 英文原文:http://jibbering.com/2002/4/httprequest.html 译文地址:http://sheneyan.com/g.php/tech/article/ajax/httprequest.html 子乌注:这篇文章我想看过的人很多,翻译的版本也有许多,我之所以要再来翻译一次,是因为这篇文章的时效性(看下一段作者说明),以及文字的

Java对象与XML文件之间相互转化(含源代码)

java对象|xml|源代码 要求:JDK1.4以上 下载:http://spaner.zj.com示例: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3