Search code examples
delphi

How to store an object to disk?


How to store an object to disk in all its glory? My object is derived from TObjectList so it holds other objects.

Which is the fastest and easiest way? Which is the compatible way?

Serialization IS NOT a solution since I want to save also non-public properties and the list of objects it holds!

For the moment I trying to save every object independently as a binary file which then are packed together. This is a lengthy process but allows me to load old versions of the object with a newer version of the program (compatibility with previous saved projects). Anyway, the complexity started to grow and it is not looking good anymore.


Solution

  • Today all my classes that need to save themselves to disk use serialization to binary files. Binary is the fastest possible method. And does not allow user intervention (as in INI/XML/CSV/Json) so the user cannot screw up the input data.

    In time I devised a library to help me with the serialization process. Seems complicated, but it isn't AT ALL.

    In the LightSaber Delphi library look up these files:

    ccStreamBuff.pas
    ccStreamFile.pas
    ccStreamMem.pas
    ccStreamMem2.pas

    Here is a simple example from the ightSaber\cbLogLinesAbstract.pas file:

      RLogLine= record
        Msg   : string;
        Level : TLogVerbLvl;
        Indent: Integer;          { How many spaces are used to indent the message }
        Bold  : Boolean;
        Time  : TDateTime;
      private
        procedure ReadFromStream_v4(Stream: TCubicBuffStream2);  { Current reader }
        procedure WriteToStream    (Stream: TCubicBuffStream2);  { Current writer }
      end;
    
    procedure RLogLine.ReadFromStream_v4(Stream: TCubicBuffStream2);
    begin
      Msg    := Stream.ReadString;
      Level  := TLogVerbLvl(Stream.ReadInteger);
      Indent := Stream.ReadInteger;
      Bold   := Stream.ReadBoolean;
      Time   := Stream.ReadDate;
      Stream.ReadPaddingE(8);  { Padding }
    end;
    
    
    procedure RLogLine.WriteToStream(Stream: TCubicBuffStream2);
    begin
      Stream.WriteString (Msg);
      Stream.WriteInteger(Ord(Level));
      Stream.WriteInteger(Indent);
      Stream.WriteBoolean(Bold);
      Stream.WriteDate(Time);
      Stream.WritePadding(8);    { Padding }
    end;
    

    The above record is loaded by the parent class TAbstractLogLines, like this:

    procedure TAbstractLogLines.ReadFromStream(Stream: TCubicBuffStream2);
    VAR
       StreamVer: Word;
    begin
      if NOT Stream.ReadHeaderVersion(StreamSign, StreamVer)
      then RAISE Exception.Create('Unknown stream signature.');
    
      case StreamVer of
              3: readFromStream_v3(Stream);
         CurVer: readFromStream_v4(Stream);
       else
         RAISE Exception.Create('Usupported stream version.');
      end;
    
      Stream.ReadPaddingDef;
    end;
    

    Observe that multiple versions can be easily loaded, which means that the code is highly extensible.

    I intend to create a video tutorial on my new Delphi channel about this.