David
David

Reputation:

Saving a TObject to a File

How can one save an Object, in its current state, to a file? So that it can immediately be read and restored with all its variables.

Upvotes: 11

Views: 14950

Answers (7)

IceCold
IceCold

Reputation: 21239

Solution 1. You can use JVCL TJvAppXMLFileStorage (see code below). But JVCL is huge!! You have to consider twice if you want or not, to drag such a huge dependency after you, your whole life.

Solution 2. Save your object to a binary file (my preferred solution). Believe me, it is not that hard. Use the ccStreamMem.pas, or even better the ccStreamBuff.pas (buffered writing) in my LightSaber Core library.
You have some code examples on how to do it, in Delphi in all its glory book.
PS: LightSaber is a lightweight alternative to JVCL.

Here is an example of how to save a record to a binary file. The operation is identical for TObject!

// Supposing you have a record (could be also an object) called RAnimationParams that you want to save to disk:

INTERFACE

USES
  System.SysUtils, Vcl.Graphics, ccStreamBuff;

TYPE
  TFrameDelayType = (fdAuto, fdUser);

  RAnimationParams = record
    Color         : TColor;          
    DelayType     : TFrameDelayType;
    procedure WriteToStream (IOStream: TCubicBuffStream);
    procedure ReadFromStream(IOStream: TCubicBuffStream);
  end;

IMPLEMENTATION

procedure RAnimationParams.WriteToStream(IOStream: TCubicBuffStream);
begin
 IOStream.WriteByte    (Ord(DelayType));
 IOStream.WriteInteger (Color);
 IOStream.WritePadding (32);
end;

procedure RAnimationParams.ReadFromStream(IOStream: TCubicBuffStream);
begin
 DelayType    := TFrameDelayType(IOStream.ReadByte);
 Color        := IOStream.ReadInteger;
 IOStream.ReadPadding (32);
end;

The padding at the end allows you to change your record/object structure later, without changing the format of your binary file.

To actually save the RAnimationParams to disk you just do:

MyBinFile:= TCubicBuffStream.CreateRead('C:\Test.bin');
AnimationParams.WriteToStream(MyBinFile);
MyBinFile.Free;

Same code when you want to load the RAnimationParams back from Test.bin, but you use CreateWrite instead of CreateRead.

The TCubicBuffStream class has even dedicated functions such as ReadHeader/CreateWrite that allow you to easily add "file magic numbers" and "file version numbers" to your binary files.

See? Not so difficult. And it will work for any object, not only for TComponent.

Update 2025: I have some new documentation here: https://gabrielmoraru.com/saving-an-object-to-disk-file/


Example for JVCL, but it won't work for TObject but only for persistent derivations:

 uses
      JvAppXMLStorage;
    
    var
      Storage: TJvAppXMLFileStorage;
    begin
      Storage := TJvAppXMLFileStorage.Create(nil);
      try
        Storage.WritePersistent('', MyObject);
        Storage.Xml.SaveToFile('S:\TestFiles\Test.xml');
        Storage.Xml.LoadFromFile('S:\TestFiles\Test.xml');
        Storage.ReadPersistent('', MyObject);
      finally
        Storage.Free;
      end;
    end;

Upvotes: 2

Herbert Sitz
Herbert Sitz

Reputation: 22266

You've already gotten some good answers to your question. Depending on what you're actually doing, it might be desirable to use a pre-built library or component to save objects. This is an inexpensive and nifty library/component set that makes it trivial to persist and restore objects, and pretty easily (i.e., with a little bit of code) accommodates persisting even unpublished members of an object: http://www.deepsoftware.ru/rsllib/index.html Not something that's rocket science, but if you're doing a lot of this sort of thing this component provides a nice framework for it.

Developer Express also includes a general purpose cxPropertiesStore component as part of the ExpressEditors library that comes with some of their components.

Upvotes: -1

Francesca
Francesca

Reputation: 21650

As already stated, the easiest way is to use a Stream and its WriteComponent and ReadComponent methods.
But be aware that :
- it works for descendants of TComponent, not plain TObject;
- only for the published properties (those saved in a dfm), not the public ones nor (a fortiori) the privwte ones;
- you have to pay a special attention for the Name property when restoring the component.

You may find some code you could use in these SO answers: Replace visual component at runtime in Delphi, Duplicating components at Run-Time

Upvotes: 3

Birger
Birger

Reputation: 4353

If you descend your object from TComponent, you can use some built-in functionality to stream the object to a file. I think this only works well for simple objects.

Some sample code to get you started:

unit Unit1;

interface

uses
  Classes;

type
  TMyClass = class(TComponent)
  private
    FMyInteger: integer;
    FMyBool: boolean;
    FMyString: string;
  public
    procedure ToFile(AFileName: string);
  published
    property MyInteger: integer read FMyInteger write FMyInteger;
    property MyString: string read FMyString write FMyString;
    property MyBool: boolean read FMyBool write FMyBool;
  end;

implementation

{ TMyClass }

procedure TMyClass.ToFile(AFileName: string);
var
  MyStream: TFileStream;
begin
  MyStream := TFileStream.Create(AFileName);
  try
    Mystream.WriteComponent(Self);
  finally
    MyStream.Free;
  end;
end;

end.

Upvotes: 3

dwc
dwc

Reputation: 24938

There's also a roll-your-own XML method here on S.O.

Upvotes: 2

JosephStyons
JosephStyons

Reputation: 58805

There is a good tutorial here. Keep in mind that you have to have RTTI (run time type information) to save an object at run-time using this approach, so it will only capture published properties of a class.

Upvotes: 1

Adrian Grigore
Adrian Grigore

Reputation: 33318

What you are looking for is called object persistance. This article might help, and there are many others if you google for "delphi persisting objects".

Upvotes: 4

Related Questions