How to serialize a TList of objects with Delphi

Some weeks ago a customer asked to me if it is possibile serialize a TList of objects. “Hey, you should use a TObjectList for this”, I said, but he absolutely needs (I dont know why) of a TList.

This is the (simple) sample code tested with Delphi XE2 Update4. Enjoy.


unit Unit4;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  Vcl.StdCtrls;

type
  TForm4 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TPerson = class
  private
    FName: String;
    procedure SetName(const Value: String);
  published
    property Name: String read FName write SetName;
  end;

var
  Form4: TForm4;

implementation


uses
  Contnrs,
  dbxjson,
  dbxjsonreflect;

{$R *.dfm}

procedure TForm4.Button1Click(Sender: TObject);
var
  list: TList;
  m: TJSONMarshal;
  json: TJSONObject;
  p1: TPerson;
  p2: TPerson;
begin
  p1 := TPerson.Create;
  p2 := TPerson.Create;
  try
    p1.Name := 'Daniele Teti';
    p2.Name := 'Peter Parker';
    list := TList.Create;
    try
      list.Add(p1);
      list.Add(p2);

      m := TJSONMarshal.Create;
      try
        // Register a specific converter for field FList
        m.RegisterConverter(TList, 'FList', function(Data: TObject; Field: String): TListOfObjects
          var
            l: TList;
            j: integer;
          begin
            l := Data as TList;
            SetLength(Result, l.Count);
            for j := 0 to l.Count - 1 do
              Result[j] := TObject(l[j]); // HardCast from pointer
          end);

        json := m.Marshal(list) as TJSONObject;
        try
          Memo1.Lines.Text := json.tostring;
        finally
          json.free;
        end;
      finally
        m.free;
      end;
    finally
      list.free;
    end;
  finally
    p1.free;
    p2.free;
  end;
end;

{ TPerson }

procedure TPerson.SetName(const Value: String);
begin
  FName := Value;
end;

end.

The output is, as expected, the following:

{"type":"System.Classes.TList","id":1,"fields":{"FList":[{"type":"Unit4.TPerson","id":2,"fields":{"FName":"Daniele Teti"}},{"type":"Unit4.TPerson","id":3,"fields":{"FName":"Peter Parker"}}],"FCount":2,"FCapacity":4}}