Delphi 2010: How to save a whole record to a file?

I have defined a record which has lots of fields with different types (integer, real , string, ... plus dynamic arrays in terms of "array of ..."). I want to save it as a whole to a file and then be able to load it back to my program. I don't want to go through saving each field's value individually. The file type (binary or ascii or ...) is not important as long Delphi could read it back to a record.

Do you have any suggestions?


You can load and save the memory of a record directly to and from a stream, as long as you don't use dynamic arrays. So if you use strings, you need to make them fixed:

type TTestRecord = record 
  FMyString : string[20]; 
end; 

var 
  rTestRecord: TTestRecord;
  strm : TMemoryStream; 

strm.Write(rTestRecord, Sizeof(TTestRecord) );

You can even load or save an array of record at once!

type TRecordArray = array of TTestRecord;

var ra : TRecordArray; 

strm.Write(ra[0], SizeOf(TTestRecord) * Length(ra));

In case you want to write dynamic content:

iCount   := Length(aArray);
strm.Write(iCount, Sizeof(iCount) );      //first write our length
strm.Write(aArray[0], SizeOf * iCount);   //then write content

After that, you can read it back:

strm.Read(iCount, Sizeof(iCount) );       //first read the length
SetLength(aArray, iCount);                //then alloc mem
strm.Read(aArray[0], SizeOf * iCount);    //then read content

As promised here it is: https://github.com/KrystianBigaj/kblib

When you defined for example record as:

TTestRecord = record
  I: Integer;
  D: Double;
  U: UnicodeString;
  W: WideString;
  A: AnsiString;
  Options: TKBDynamicOptions;

  IA: array[0..2] of Integer;

  AI: TIntegerDynArray;
  AD: TDoubleDynArray;
  AU: array of UnicodeString;
  AW: TWideStringDynArray;
  AA: array of AnsiString;

  R: array of TTestRecord; // record contain dynamic array of itself (D2009+)
end;

You can save whole dynamic record to stream (as binary data) by :

TKBDynamic.WriteTo(lStream, lTestRecord, TypeInfo(TTestRecord));

To load it back:

TKBDynamic.ReadFrom(lStream, lTestRecord, TypeInfo(TTestRecord));

It not need to be a record, you can do same for any dynamic type like:

TKBDynamic.WriteTo(lStream, lStr, TypeInfo(UnicodeString));
TKBDynamic.WriteTo(lStream, lInts, TypeInfo(TIntegerDynArray));
TKBDynamic.WriteTo(lStream, lArrayOfTestRecord, TypeInfo(TArrayOfTestRecord)); // TArrayOfTestRecord = array of TTestRecord;

Tested on Delphi 2006/2009/XE. License: MPL 1.1/GPL 2.0/LGPL 3.0 See readme for information.


Another option which works very well for records (Delphi 2010+) is to use the SuperObject library. For example:

type
  TData = record
    str: string;
    int: Integer;
    bool: Boolean;
    flt: Double;
  end;
var
  ctx: TSuperRttiContext;
  data: TData;
  obj: ISuperObject;
  sValue : string;
begin
  ctx := TSuperRttiContext.Create;
  try
    sValue := '{str: "foo", int: 123, bool: true, flt: 1.23}';
    data := ctx.AsType<TData>(SO(sValue));
    obj := ctx.AsJson<TData>(data);
    sValue := Obj.AsJson;
  finally
    ctx.Free;
  end;
end;

I also tested this briefly with a simple TArray<Integer> dynamic array and it did not have a problem storing and loading the array elements.