如何将对象存储到磁盘?

时间:2010-02-28 14:17:59

标签: delphi

如何将对象存储到磁盘中的所有荣耀? 我的对象派生自TObjectList,因此它包含其他对象。

哪种方式最快最简单?哪种兼容方式?

序列化不是解决方案,因为我还要保存非公共属性及其拥有的对象列表!

目前我尝试将每个对象独立保存为二进制文件,然后将其打包在一起。这是一个漫长的过程,但允许我使用较新版本的程序加载旧版本的对象(与以前保存的项目兼容)。无论如何,复杂性开始增长,并且它看起来不再好看了。

8 个答案:

答案 0 :(得分:4)

如果您使用的是Delphi 2010,由于新的RTTI单元,事情将变得更加容易,Robert Love编写了一个很好的单元来将对象序列化为XML,称为XMLSerial

您可以在他的博客上阅读:Xml Serialization - Basic Usage

答案 1 :(得分:2)

我也主要使用手工序列化来构建我自己的数据结构。多版本角度是主要原因之一。

但是在您的情况下很难,因为并非所有对象(tobjectlist)都来自包含要加载/存储的虚拟抽象方法的自己的层次结构。

D2010序列化(afaik允许几乎所有东西到RTTI)可能是一个解决方案,但可能需要一个新的delphi版本,更糟糕的是,它结束了手动处理版本控制。 (例如,当格式改变时,将旧字段中的值复制到新字段中)

如果手动流式传输失控,不同的方法可能是对对象的数据部分进行抽象定义,并从这些抽象定义生成源代码(字段声明和流式代码)。优点是,您可以在需要时随时随地使用某些自定义代码,或者为版本问题修补生成器。

我做过一次这样的业务对象,用于超过800个对象的SQL映射。既然是Delphi中泛型的时候,我也为每个对象生成了一个类型安全的容器类型,以及其他帮助器和转换器对象/例程。

虽然设置很多工作,但是如果你的项目中包含很多对象和字段(数百甚至数千个)并且确定你需要保持它有重大突变,那么这是值得的。很长一段时间。

答案 2 :(得分: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对象从流组件中流出。

不是一个完整的解决方案,但它应该让你想要去做更多的工作。

答案 3 :(得分:2)

如果您将在多年内拥有该对象的单独版本,那么您当前的解决方案可能是最好的。

我所做的是创建SaveToStream()和LoadFromStream()方法,并以固定顺序手动将对象的属性写入tstream,并在其前面加上结构的版本号。正如您所提到的,这样做的好处是您可以更好地适应旧版本的流。例如,如果您有5个版本,但需要以某种方式为版本3文件初始化某些内容,则可以轻松完成。然后围绕它创建一个SaveToFile()函数,创建一个TFileStream并调用SaveToStream()。

我相信有一个TWriter类可以让你更轻松地将各种数据类型写入流中......或者你可以创建自己的数据类型。 (我制作了自己的文件流后代来处理这个问题)

如果将多个对象保存到单个流中,您可能需要在每个对象写入之前记下位置,然后返回并标记长度,以便您(或访问文件的人)可以跳过文件不读它。

此外,如果您要保存要保存的类层次结构,请将具有要保存到文件的所有属性的ancester类“底部加载”。这样您只需要一个保存例程的实现。它的效率稍低,因为你携带的变量在所有对象中都不一定需要,但它更易于管理。

答案 4 :(得分:1)

  

目前我试图拯救每一个人   作为二进制文件独立对象   然后被打包在一起。

我认为这是一个很好的方法。它可能需要一些编码,但速度很快!此外,您可以稍后轻松升级它 - 以防您需要更改文件格式。要轻松升级格式,请不要忘记在文件中留下一些填充字节。您可以稍后在其中添加其他信息,而无需实际更改文件格式。

答案 5 :(得分:0)

您需要想出一些将该对象编码为字符串的方法,以便在使用该字符串构造新对象时获得相同的对象。这称为序列化,有些语言为您提供了这些功能。

例如,如果你有这个对象:

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

且a = 5,b = 3.2,c = 67.5

你的字符串可能如下所示:

a5b3.2c67.5

然后你可以解析该字符串并为所有成员分配适当的值。

我认为不用说字符串很容易存储在磁盘上。

修改 这是一个非常基本的例子,但我认为你可以很容易地理解这个概念。

修改 Delphi specific serialization。在页面底部有一个指向完整的XML序列化程序类的链接。

答案 6 :(得分:0)

很多这样做的方法我猜,过去我曾经使用过inifiles用于此目的,因为TMemInifile可以让你在保存到磁盘之前在内存中做很多繁重的工作。由于您有一个可以省略的层次结构,因此您可能需要考虑使用类似XML的内容,但我没有亲自完成此操作,因此无法就此进行建议。

答案 7 :(得分:0)

如果对象是从TPersistent派生的,那么使用开源库很容易实现XML或JSON序列化:

使版本控制更容易的一种方法是在域模型和持久层之间使用anti-corruption layer。 (可能使用的数据传输对象不会随着域模型的每次更改而改变。)

对于自动版本控制,请参阅此文章:Migrate Serialized Java Objects with XStream and XMT

XMT将类VersionedDocument引入版本序列化的XML并处理迁移。使用Delphi可以轻松实现相同的设计。