2010-02-28 320 views
14

如何將對象存儲到磁盤的所有榮耀? 我的對象是從TObjectList派生的,所以它包含其他對象。如何將對象存儲到磁盤?

哪種方法最快最簡單?這是兼容的方式?

序列化不是一個解決方案,因爲我想保存非公有屬性及其保存的對象列表!

目前我試圖將每個對象獨立保存爲一個二進制文件,然後將它們打包在一起。這是一個漫長的過程,但允許我使用較新版本的程序加載舊版本的對象(與以前保存的項目兼容)。無論如何,複雜性開始增長,並且看起來不再好。

+1

+1這是一個非常好的問題,在RTTI中,元數據僅適用於已發佈的部分字段,但從另一方面來說,調試檢查器顯示所有類字段,因此它必須是一種方法。 – 2010-02-28 17:41:29

+0

爲什麼你需要爲每個對象使用一個文件?當然這會更慢,更容易出錯。 – mghie 2010-02-28 18:06:52

+0

「爲什麼你需要爲每個對象使用一個文件?」 ----你是對的,但我只是在這個開始。它會以某種方式演變爲單個文件。 – Ampere 2010-03-02 15:44:09

回答

2

我也大多使用手工系列化我自己的數據結構。多版本角度是主要原因之一。

但是在你的情況下,這很困難,因爲並不是所有的對象(tobjectlist)都是從包含虛擬抽象方法來加載/存儲的自己的層次派生而來的。

D2010的序列化(afaik允許幾乎所有的RTTI)可能是一個解決方案,但可能需要一個新的delphi版本,更糟的是,它終結了手動處理版本。 (例如,當格式改變時,將舊字段的值複製到新字段中)

如果手動流式傳輸失控,不同的方法可能是讓對象的數據部分具有抽象定義,並生成源代碼(來自這些抽象定義的字段聲明和流媒體代碼)。優點是您可以在需要時在某處自定義代碼,或者爲發生版本問題修補發生器。

我曾經爲一個業務對象做過一次SQL映射,超過800個對象。由於是Delphi中的泛型,所以我爲每個對象以及其他幫助程序和轉換器對象/例程生成了一個類型安全的容器類型。

雖然設置的工作量很大,但如果你有一個真正有很多對象和領域的項目(數百個,如果不是數千個),並且確信你需要維護它突變在相當長的一段時間。

+0

「不過你的情況是困難的,因爲不是所有的對象(tobjectlist)從自己的層次結構包含虛擬抽象方法來加載/存儲派生。」這就是接口起作用的地方。序列化與實現繼承無關。讓所有的對象都實現一個'ISerializable',你很好走。 – mghie 2010-02-28 18:00:21

+0

當然,可能。 (儘管你必須自己實現IUnknown方法,並且可能需要多次執行不同根層次的層次,因爲tobjectlist不會從tinerfacedobject繼承)。 – 2010-02-28 18:37:13

0

您需要想出一些將該對象編碼爲字符串的方式,以便在您使用該字符串構造新對象時獲得相同的對象。這稱爲序列化,並且一些語言爲您提供了該功能。

例如,如果你有這樣的對象:

class serialize_me { 
private: 
    int a; 
    float b; 
public: 
    double c; 
} 

和1 = 5,B = 3.2,和c = 67.5

您的字符串可能是這樣的:

a5b3.2c67 .5

然後,您可以解析該字符串並將適當的值分配給所有成員。

我認爲這不言而喻,字符串很容易存儲在磁盤上。

編輯: 這是一個非常基本的例子,但我認爲你可以很容易地理解這個概念。

編輯: Delphi specific serialization。在頁面底部有一個鏈接到一個完整的XML序列化程序類。

+1

當你的對象包含其他對象時你會做什麼? – Ampere 2010-02-28 15:06:54

+2

@Altar:遞歸。 – 2010-02-28 15:12:56

+0

「Delphi特定序列化」僅限於公共/已發佈的屬性。 – Ampere 2010-02-28 16:46:31

0

很多方法可以做到這一點我想,在過去,我已經爲此使用了inifiles,因爲TMemInifile有助於您在存儲到磁盤之前在內存中執行大量繁重的工作。既然你有一個層次來存儲,你可能會考慮使用類似XML的東西,但是我沒有親自做過,所以不能就此提出建議。

0

如果對象從TPersistent派生,XML或JSON系列化很容易與開源庫做:

的一種方式,使版本更容易是anti-corruption layer域模型和持久層之間。 (可能使用的數據傳輸對象不會隨着域模型的每次更改而改變)。

對於自動版本看到的這篇文章:Migrate Serialized Java Objects with XStream and XMT

XMT介紹類VersionedDocument到版本序列化的XML,並處理遷移。 Delphi可以很容易地實現相同的設計。

+1

OmniXML與我之前發現的任何其他序列化方法一樣有限。他們說:「注意:類應該從TPersistent派生而來,並且應該發佈屬性」。序列化並不是一個真正的解決方案,用於將整個對象存儲到磁盤,但只有一部分(已發佈的屬性)。 – Ampere 2010-02-28 17:22:00

+1

通過已發佈屬性的序列化適用於整個VCL;沒有理由不使用它。如果您可以控制對象的構成,請考慮添加已發佈的屬性以公開您需要序列化的內容。發佈的屬性不必與基礎私有成員具有相同的類型,例如,你有一個私人TDateTime字段,但通過公佈的道具將其作爲字符串公開。如果不是這樣,您可以爲每個對象類添加一個讀/寫.AsString屬性,以便每個對象讀取和寫入它自己的狀態。無論哪種方式,你必須做*某事*。 – 2010-02-28 17:37:17

+0

@Altair + 1你是對的,序列化應該包括對象的所有字段而不管可見性(因爲這是序列化的全部目的:保存完整的對象狀態)。德爾福過去僅限於發佈和(使用M美元)公共領域。希望藉助新的擴展RTTI,圖書館將縮小這一差距。 – mjn 2010-02-28 17:56:09

4

如果您使用的是Delphi 2010,由於新的RTTI單元,事情將變得更加容易,Robert Love已經編寫了一個很好的單元來將對象序列化爲XML,稱爲XMLSerial

你可以在他的博客讀到它:Xml Serialization - Basic Usage

+0

這與我迄今發現的任何其他序列化方法一樣有限,因爲屬性應該發佈。序列化並不是一個真正的解決方案,用於將整個對象存儲到磁盤,但只有一部分(已發佈的屬性)。 – Ampere 2010-02-28 17:23:54

+0

您是否真的檢查過D2010角度?,因爲RTTI代改變了! – 2010-02-28 17:35:06

+0

@Mohammad也jvAppXMLFileStorage可以幫助,但只適用於發佈的字段。 @ Delphi2010中的Marco也GetPropList只返回已發佈的屬性。 – 2010-02-28 17:56:02

2

你聲明序列化不是解決方案,但我問爲什麼不呢?過去我做過這樣的事情,但這是我所做的。

我創建了一個組件類,它什麼都不做,只是序列化一個非TPersistant的對象,這樣我就可以使用VCL流式傳輸功能對它進行流式處理。

例如:

//請原諒我的存在,因爲我想從我的頭頂鍵入此的任何錯誤。而且,這不會在功能上完成。

unit streamlist1; 

interface 

uses MyListObjectUnit; 

procedure SaveList(fielname:string; data:TMyListObject); 
procedure LoadList(filename:string; var data:TMyListObject); 

implementation 

type 
    TMyListStreamer = class(TComponent) 
    private 
    fMyList : TMyListObject; 
    procedure ReadList(Reader:TReader); //This is where the magic happens 
    procedure WriteList(Writer: TWriter); //This is where the magic happens (x2) 
    public 
    procedure DefineProperties(Filer: TFiler); override; //defined in TPersistent 
    procedure AssignMyList(data:TMyListObject); 
    procedure PopulateData(var data:TMyListObject); 
    end; 


TMyListStreamer.procedure DefineProperties(Filer: TFiler); override; //defined in TPersistent 
begin 
    Filer.DefineProperty('MyObjList', ReadList, WriteList, true); 
    //Filer.DefineBinaryProperty('MyObjList', ReadList, WriteList, true); //your choice 
end; 

procedure TMyListStreamer.ReadList(Reader:TReader); //This is where the magic happens 
begin 
    //Use the reader class to read in anything you want... 
end; 

procedure TMyListStreamer.WriteList(Writer: TWriter); //This is where the magic happens (x2) 
begin 
    //Use the writer class to write out anything you want... 
end; 

procedure SaveList(fielname:string; data:TMyListObject); 
var 
    wFile : TFileStream; 
    wList : TMyListStreamer; 
begin 
    RegisterClass(TMyListStreamer); 
    Try 
    wFile := TFileStream.Create(filename, fmcreate); 
    wList := TMyListStreamer.create(nil); 
    try 
     wList.AssignMyList(Data); 
     wFile.WriteComponent(wList); 
    finally 
     wFile.Free; 
     wList.free; 
    end; 
    finally 
    Unregisterclass(TMyListStreamer); 
    end; 
end; 

procedure LoadList(filename:string; var data:TMyListObject); 
var 
    wFile : TFileStream; 
    wList : TMyListStreamer; 
begin 
    RegisterClass(TMyListStreamer); 
    Try 
    wFile := TFileStream.Create(filename, fmOpenRead); 
    try 
     wList := TMyListStreamer(wFile.ReadComponent(Nil)); 

     if assigned(data) and assigned(wList) then 
     wList.PopulateData(data); 

     if assigned(wList) then 
     wList.free; 
    finally 
     wFile.Free; 
    end; 
    finally 
    Unregisterclass(TMyListStreamer); 
    end; 
end; 

使用此方法,您可以對VCL或自定義數據中的任何內容進行流式處理(序列化)。 需要一點設置,但其優點是可以控制進出數據文件的所有內容。您甚至可以稍微思考一下,通過忽略或按新程序/組件的新版本中的特定數據來創建版本標誌並處理不同的數據。

只要您已經通過使用TReader/TWriter的現有方法知道對象的類型(即基於TComponent/TPersistant的對象),您甚至可以將其他VCL對象從流組件中流出。

不是一個完整的解決方案,但它應該讓你在哪裏你想要多一點工作。

+1

>你聲明序列化不是解決方案,但我問爲什麼不呢? ------
到目前爲止,我發現的所有流媒體解決方案都只能保存已發佈的屬性。有些人有煩惱明白節約大約一個對象的屬性是不一樣的保存整個對象(這樣以後就可以恢復原來的狀態):) – Ampere 2010-03-02 11:50:04

+0

雖然我還沒有提供完整的解決方案,使用這種方法將允許你保存任何你想要的。我認爲大多數其他答案都傾向於同樣的東西,但對同一個問題的不同答案是編程的一大優點。我喜歡這個愛好^ H^H^H^H^H工作。 – 2010-03-02 14:26:08

+0

>「即使我沒有提供完整的解決方案,使用這種方法將允許您保存任何你想要的東西」。 - - 是的我知道。我看過你的代碼。這就是爲什麼我使用「直到現在」。我仍然在加重你的解決方案和GrandmasterB提出的解決方案。 – Ampere 2010-03-02 15:06:53

2

多年來,如果您打算使用單獨版本的對象,您當前的解決方案可能是最好的解決方案。

我所做的是創建SaveToStream()和LoadFromStream()方法,並手動將對象的屬性以固定順序寫入tstream中,並以結構的版本號作爲前綴。正如您所提到的,這樣做的好處是您可以更好地適應舊版本的流。例如,如果您有5個版本,但您需要以某種方式爲版本3文件初始化某些內容,則很容易完成。然後,圍繞它包裝SaveToFile()函數,創建一個TFileStream並調用SaveToStream()。

我相信有一個TWriter類可以讓你更容易地將各種數據類型寫入流中......或者你可以創建自己的數據類型。 (我做了自己的文件流後代來處理這個)

如果將多個對象保存到單個流中,您可能需要在每個對象寫入之前記下位置,然後返回並標記長度,以便您或有人訪問這些文件)可以在不讀取文件的情況下跳過文件。

此外,如果您有要保存的類的層次結構,請將包含要保存到文件中的所有屬性的「上下文加載」類。這樣你只需要執行一次保存程序。它的效率稍低一點,因爲你攜帶了所有對象不一定需要的變量,但其管理起來要簡單得多。

+0

嗨大師。我想我會繼續走這條路。謝謝。 – Ampere 2010-03-02 12:33:18

1

目前我試圖獨立節省每 對象,然後擠在一起的二進制文件 。

我認爲這是一個很好的方法。它可能需要一些編碼,但速度很快!另外,您稍後可以輕鬆升級它 - 以防您需要對文件格式進行更改。爲了便於格式升級,不要忘記在文件中留下一些填充字節。您稍後可以在其中添加其他信息,而無需實際更改文件格式。