Delphi 如何序列化和反序列化 TObject

问题描述 投票:0回答:1

我有以下处理

TStream
写入和读取的代码,但我的问题(问题)是使用
TILObjects
类读取/写入
TListHelper
。请阅读这些方法中的注释:

procedure TListHelper<T>.Serialize(const Buffer: IBuffer; const List: IList<T>);
function TListHelper<T>.Deserialize(const Buffer: IBuffer): IList<T>;
type
  IBuffer = interface
    procedure WriteInteger(Value: Integer);
    function ReadInteger:Integer
  end;

  TBuffer = class(TInterfacedObject, IBuffer)
  private
    FStream: TStream;
  public
    constructor Create(AStream: TStream);
    procedure WriteInteger(Value: Integer);
    function ReadInteger: Integer;
  end;
  
  type
  ILObject = interface
    ['{D4E3A925-58F2-4E8C-93E7-B49C1C3B453D}']
    procedure Serialize(Stream: IBuffer);
    function Deserialize(Stream: IBuffer): ILObject;
  end;


  TILObject = class(TInterfacedObject, ILObject)
  public
    procedure Serialize(Stream: IBuffer); virtual; abstract;
    function Deserialize(Stream: IBuffer): ILObject; virtual; abstract;
  end;

type
  IListHelper<T> = interface
    procedure Serialize(const Buffer: IBuffer; const List: IList<T>);
    function Deserialize(const Buffer: IBuffer): IList<T>;
  end;

  TListHelper<T> = class(TInterfacedObject, IListHelper<T>)
  public
    procedure Serialize(const Buffer: IBuffer; const List: IList<T>);
    function Deserialize(const Buffer: IBuffer): IList<T>;
  end;
// test classes
TChild = class(TILObject)
  private
  public
    constructor Create; overload; override;
    destructor Destroy; override;
    procedure Serialize(Stream: IBuffer); override;
    class function Deserialize(const Stream: IBuffer): TChild; reintroduce; overload;
  end;
  
  TFather = class(TILObject)
  private
    FChildList: IList<TChild>;
   procedure ReadChild(const Stream: IBuffer);
  public
    constructor Create; overload; override;
    destructor Destroy; override;
    procedure Serialize(Stream: IBuffer); override;
    class function Deserialize(const Stream: IBuffer): ILObject; reintroduce; overload;
    property ChildList: IList<TChild> read FChildList write FChildList;
  end;

// 
implementation

constructor TBuffer.Create(AStream: TStream);
begin
  inherited Create;
  FStream := AStream;
end;

procedure TBuffer.WriteInteger(Value: Integer);
begin
  FStream.WriteBuffer(Value, SizeOf(Value));
end;

function TBuffer.ReadInteger: Integer;
begin
  FStream.ReadBuffer(Result, SizeOf(Result));
end;

procedure TListHelper<T>.Serialize(const Buffer: IBuffer; const List: IList<T>);
var
  Count, i: Integer;
begin
  Count := List.Count;
  Buffer.WriteInteger(Count);

  for i := 0 to Count - 1 do
  begin
    if TypeInfo(T) = TypeInfo(Integer) then
      Buffer.WriteInteger(Integer(TValue.From<T>(List[I]).AsType<Integer>))
// here how deal with the TILObjects.Serialize
// I want to have TILObject.Serialize(Buffer)
  // else if TILObject
    else
      raise Exception.Create('Unsupported type for List serialization');
  end;
end;

function TListHelper<T>.Deserialize(const Buffer: IBuffer): IList<T>;
var
  Count, i: Integer;
  Item: TValue;
  LObject: TILObject;
begin
  Result := TList<T>.Create;
  Count := Buffer.ReadInt32;

  for i := 0 to Count - 1 do
  begin
    if TypeInfo(T) = TypeInfo(Integer) then
      Item := TValue.From<Integer>(Buffer.ReadInteger)
    // here how to deal with TILObject.Deserialize
   // else if  TILObject.Deserialize(Buffer)
    else
      raise Exception.Create('Unsupported type for List deserialization');

    Result.Add(Item.AsType<T>);
  end;
end;

// test classes
constructor TChild.Create;
begin
  inherited Create;
end;

destructor TChild.Destroy;
begin
  inherited Destroy;
end;

procedure TChild.Serialize(Stream: IBuffer);
begin
  inherited Serialize(Stream);
end;

constructor TFather.Create;
begin
  inherited Create;
  FChildList := TList<TChild>.Create;
end;

class function TChild.Deserialize(const Stream: IBuffer): ILObject;
begin
  Result := TChild.Create;
end;

如何在

TIlObject.Serialize()
内使用
TListHelper<T>.Serialize()
和在
TILObject.Deserialize()
内使用
TListHelper<T>.Deserialize()

delphi serialization deserialization
1个回答
0
投票

你可以尝试这样的事情:

procedure TListHelper<T>.Serialize(const Buffer: IBuffer; const List: IList<T>);
var
  Count, i: Integer;
  val: TValue;
  LIntf: ILObject;
  LObject: TObject;
begin
  Count := List.Count;
  Buffer.WriteInteger(Count);

  for i := 0 to Count - 1 do
  begin
    val := TValue.From<T>(List[I]);
    case GetTypeKind(T) of 
      tkInteger: begin
        Buffer.WriteInteger(val.AsType<Integer>);
      end;
      tkInterface: begin
        if Supports(val.AsInterface, ILObject, LIntf) then
          LIntf.Serialize(Buffer)
        else
          raise Exception.Create('Unsupported interface type for List serialization');
      end;
      tkClass: begin
        LObject := val.AsObject;
        if LObject.InheritsFrom(TILObject) then
          TILObject(LObject).Serialize(Buffer)
        else
          raise Exception.Create('Unsupported object type for List serialization');
      end;
    else
      raise Exception.Create('Unsupported type for List serialization');
    end;
  end;
end;

function TListHelper<T>.Deserialize(const Buffer: IBuffer): IList<T>;
var
  Count, i: Integer;
  Item: TValue;
  LIntf: ILObjct;
  LObject: TILObject;
begin
  Result := TList<T>.Create;
  Count := Buffer.ReadInt32;

  for i := 0 to Count - 1 do
  begin
    case GetTypeKind(T) of
      tkInteger: begin
        Item := TValue.From<T>(Buffer.ReadInteger);
      end;
      tkInterface: begin
        LIntf := ???.Create as ILObject;
        LIntf.Deserialize(Buffer);
        Item := TValue.From<T>(LIntf);
      end;
      tkClass: begin
        LObject := ???.Create as ILObject;
        LObject.Deserialize(Buffer);
        Item := TValue.From<T>(LObject);
      end;
    else
      raise Exception.Create('Unsupported type for List deserialization');
    end;

    Result.Add(Item.AsType<T>);
  end;
end;

但是,正如你所看到的,

TListHelper.Deserialize()
有一个问题。您必须首先创建一个对象实例才能在其上调用
(T)ILObject.Deserialize()
,因此您需要知道要创建哪种类类型。您无法调用
T.Create
,因为
T
没有
class
约束。因此,您必须将类名写入
Buffer
,将其读回,然后使用单独的工厂从指定的类名创建并返回
(T)ILObject
实例,例如:

procedure TListHelper<T>.Serialize(const Buffer: IBuffer; const List: IList<T>);
var
  Count, i: Integer;
  val: TValue;
  LIntf: ILObject;
  LObject: TObject;
begin
  Count := List.Count;
  Buffer.WriteInteger(Count);

  for i := 0 to Count - 1 do
  begin
    val := TValue.From<T>(List[I]);
    case GetTypeKind(T) of 
      tkInteger: begin
        Buffer.WriteInteger(val.AsType<Integer>);
      end;
      tkInterface: begin
        if Supports(val.AsInterface, ILObject, LIntf) then
        begin
          Buffer.WriteString((LIntf as TObject).ClassName);
          LIntf.Serialize(Buffer);
        end else
          raise Exception.Create('Unsupported interface type for List serialization');
      end;
      tkClass: begin
        LObject := val.AsObject;
        if LObject.InheritsFrom(TILObject) then
        begin
          Buffer.WriteString(LObject.ClassName);
          TILObject(LObject).Serialize(Buffer)
        end else
          raise Exception.Create('Unsupported object type for List serialization');
      end;
    else
      raise Exception.Create('Unsupported type for List serialization');
    end;
  end;
end;

function TListHelper<T>.Deserialize(const Buffer: IBuffer): IList<T>;
var
  Count, i: Integer;
  Item: TValue;
  LClassName: string;
  LIntf: ILObjct;
  LObject: TILObject;
begin
  Result := TList<T>.Create;
  Count := Buffer.ReadInt32;

  for i := 0 to Count - 1 do
  begin
    case GetTypeKind(T) of
      tkInteger: begin
        Item := TValue.From<T>(Buffer.ReadInteger);
      end;
      tkInterface: begin
        LClassName := Buffer.ReadString;
        LIntf := CreateILObject(LClassName) as ILObject; // <--
        LIntf.Deserialize(Buffer);
        Item := TValue.From<T>(LIntf);
      end;
      tkClass: begin
        LClassName := Buffer.ReadString;
        LObject := CreateILObject(LClassName); // <--
        LObject.Deserialize(Buffer);
        Item := TValue.From<T>(LObject);
      end;
    else
      raise Exception.Create('Unsupported type for List deserialization');
    end;

    Result.Add(Item.AsType<T>);
  end;
end;

您必须自己实现

CreateILObject()
,例如:

function CreateILObject(AClassName: string): TILObject;
begin
  if AClassName = 'TChild' then
    Result := TChild.Create
  ...
  else
    Result := nil; // or raise
end;

有多种方法可以概括这样的工厂,但这超出了这个问题的范围。 StackOverflow(和其他论坛)上还有许多与(反)序列化对象相关的其他问题。

© www.soinside.com 2019 - 2024. All rights reserved.