用DELPHI的RTTI实现对象的XML持久化 【转】
去年我花了很多時間嘗試用DELPHI進(jìn)行基于XML的WEB應(yīng)用開發(fā)。起初的設(shè)想是很美好的,但結(jié)果做出來的東西很簡陋。一部分原因就在于XML到Object之間的數(shù)據(jù)綁定實現(xiàn)太麻煩(另一部分是因為對XSLT不熟,學(xué)習(xí)它花了很多時間)。
????? 之前我一直是用DELPHI提供的XML Data binding來做的,基本做法是:先用工具(如XMLSPY)做好一個XML Schema(XSD),然后用XML Data binding生成DELPHI的接口和類。當(dāng)然,一旦生成好就很方便了,在程序里我只要操作這個接口就好了,其中各個Field都會被變成屬性,并且類型也都如我在XSD中的定義。但問題在于程序在開發(fā)過程中,總是會有一些變化的,在這種情況下,我就不得不同時開著XMLSPY修改XSD,然后重新用 XML Data binding的Wizard跑一遍,非常的麻煩。
????? 所以當(dāng)我想到數(shù)據(jù)集的對象化后,立即想到也可以用RTTI來實現(xiàn)Object的XML持久化--其實DELPHI6開始的SOAP實現(xiàn)就是用RTTI來實現(xiàn)Object到SOAP數(shù)據(jù)(就是XML)的轉(zhuǎn)換的。顯然我已經(jīng)是非常的后知后覺了,當(dāng)我在《強大的DELPHI RTTI--兼談需要了解多種開發(fā)語言》一文中說到我的打算時,朋友Lex CHow回復(fù)我說他在大約一年前就做過了這方面的工作,我當(dāng)即跟他要來了他的源碼。lexlib是他寫的是一個有很多功能的庫,看上去結(jié)構(gòu)有點像.net 的基本類庫(當(dāng)然沒那么大^O^),Object的XML持久化只是其中的很小的一部分。因為我只需要這一部分,就沒必要用這整個一個庫這么麻煩,于是參考了lexlib并結(jié)合我在《用DELPHI的RTTI實現(xiàn)數(shù)據(jù)集的簡單對象化》中已經(jīng)實現(xiàn)的部分,做了一個簡單的實現(xiàn):
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 DoBeginpInfo := pList.Props[i];If ( pInfo^.PropType^.Kind = tkClass ) ThenBegintmpObj := TObject( Integer( GetPropValue( aObj, pInfo^.Name ) ) );If ( Assigned( tmpObj ) AND ( tmpObj Is TPersistent ) ) ThenLoadObjFromXML( aNode.ChildNodes[WideString(pInfo^.Name)],tmpObj As TPersistent );EndElse If ( pInfo^.PropType^.Kind In DefaultFilter ) ThenSetPropValue( 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 DoBeginpInfo := pList.Props[i];If ( pInfo^.PropType^.Kind = tkClass ) ThenBegintmpObj := TObject( Integer( GetPropValue( aObj, pInfo^.Name ) ) );If ( Assigned( tmpObj ) AND ( tmpObj Is TPersistent ) ) ThenSaveObjToXML( aNode.AddChild( WideString( pInfo^.Name ) ),tmpObj As TPersistent );EndElse If ( pInfo^.PropType^.Kind In DefaultFilter ) ThenaNode.AddChild( WideString( pInfo^.Name ) ).Text :=GetPropValue( aObj, pInfo^.Name );End;FinallypList.Free;End;End; end;????? 這個實現(xiàn)應(yīng)該說是很簡單的。主要是三個部分(Load和Save的結(jié)構(gòu)是相似的):
????? 一是對TMDataSetProxy作特別處理,委托給這個類自己去處理它的實現(xiàn),因為它與一般的類不同,需要通過ForEach遍歷全部記錄,這其實就是同時實現(xiàn)數(shù)據(jù)集的XML持久化。
????? 二是對Class作遞歸處理,當(dāng)然只支持從TPersistent派生的class。
????? 三是一般的Field簡單地轉(zhuǎn)成String保存,其中借鑒了lexlib的Filter,只處理那些能簡單地轉(zhuǎn)成String的數(shù)據(jù)類型,過濾掉那些可能造成轉(zhuǎn)換出錯的類型。
????? 上面的代碼中用到的TMPropList見《用DELPHI的RTTI實現(xiàn)數(shù)據(jù)集的簡單對象化》中的實現(xiàn)。
????? 下面是用TMDataSetProxy實現(xiàn)的數(shù)據(jù)集的XML持久化。免去了需要通過TClientDataSet進(jìn)行的麻煩,并且采用的是用Node記錄字段的方式,.net也是采用這樣的方式,與TClientDataSet所用的用Attribute記錄字段的方式不同。雖然這樣生成的 XML文件將會略大一些,但是好處也是顯而易見的,特別是我是準(zhǔn)備用在Web應(yīng)用中的,用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 ) ThenSetVariant( 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 ) ThenpRow.AddChild( WideString( pInfo^.Name ) ).Text:= GetVariant( i );End;End; end;????? 下面是一個簡單的DEMO,其中包括了對數(shù)據(jù)集的XML持久化。注意Load的時候Employee成員連接的是ADODataSet2,它連接到一個包含了這幾個字段的表,各字段類型與Employee表相同,但內(nèi)容為空,并且去掉了EmployeeID的Identity。Load完成后,Employee表中這幾個字段的內(nèi)容將被復(fù)制到此表中。
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了--雖然有好用的工具,但能省點事終歸是好的。
總結(jié)
以上是生活随笔為你收集整理的用DELPHI的RTTI实现对象的XML持久化 【转】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 强调团体与配合的jinbiguandan
- 下一篇: 飞鸽传书2014怎么用?