貼)用DELPHI的RTTI實現對象的XML持久化

來源:互聯網
上載者:User
去年我花了很多時間嘗試用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了--雖然有好用的工具,但能省點事終歸是好的。

猛禽 Jan.29-05 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.