gpt4 book ai didi

delphi - 泛型和 Marshal/UnMarshal。我在这里缺少什么?第 2 部分 :-)

转载 作者:行者123 更新时间:2023-12-03 15:33:32 24 4
gpt4 key购买 nike

跟进我之前的问题: Generics and Marshal / UnMarshal. What am I missing here?

在“第 1 部分”(上面的链接)中,TOndrej 提供了一个很好的解决方案 - 但在 XE2 上失败了。在这里我提供了更正的来源来纠正这个问题。

我觉得有必要进一步扩展这个问题。所以我想听听大家如何做到这一点:

首先 - 要使源代码在 XE2 和 XE2 更新 1 上运行,请进行以下更改:

Marshal.RegisterConverter(TTestObject,
function (Data: TObject): String // <-- String here
begin
Result := T(Data).Marshal.ToString; // <-- ToString here
end
);

为什么?我认为与 XE2 相关的唯一原因是有更多的 RTTI 信息可用。因此它将尝试编码(marshal)返回的 TObject。我走在正确的轨道上吗?欢迎大家发表评论。

更重要 - 该示例未实现 UnMarshal 方法。如果有人可以制作一个并将其发布在这里,我会很高兴:-)

我希望您仍然对这个主题感兴趣。

亲切的问候比亚内

最佳答案

除了这个问题的答案之外,我还在这里发布了您上一个问题的解决方法:Generics and Marshal / UnMarshal. What am I missing here?

出于某种原因,使用 TJsonobject 的非默认构造函数会导致 XE2 中出现问题 - 使用默认构造函数“修复”了该问题。

首先,您需要将 TTestobject 移动到其自己的单元 - 否则,RTTI 在尝试解码时将无法找到/创建您的对象。

    unit uTestObject;

interface

uses
SysUtils, Classes, Contnrs, Generics.Defaults, Generics.Collections, DbxJson, DbxJsonReflect;

type
{$RTTI EXPLICIT METHODS([]) PROPERTIES([vcPublished]) FIELDS([vcPrivate])}
TTestObject=class(TObject)
private
aList:TStringList;
public
constructor Create; overload;
constructor Create(list: array of string); overload;
constructor Create(list:TStringList); overload;
destructor Destroy; override;
function Marshal:TJSonObject;
class function Unmarshal(value: TJSONObject): TTestObject;
published
property List: TStringList read aList write aList;
end;

implementation

{ TTestObject }

constructor TTestObject.Create;

begin
inherited Create;
aList:=TStringList.Create;
end;

constructor TTestObject.Create(list: array of string);

var
I:Integer;

begin
Create;
for I:=low(list) to high(list) do
begin
aList.Add(list[I]);
end;
end;

constructor TTestObject.Create(list:TStringList);

begin
Create;
aList.Assign(list);
end;

destructor TTestObject.Destroy;

begin
aList.Free;
inherited;
end;

function TTestObject.Marshal:TJSonObject;

var
Mar:TJSONMarshal;

begin
Mar:=TJSONMarshal.Create();
try
Mar.RegisterConverter(TStringList,
function(Data:TObject):TListOfStrings

var
I, Count:Integer;
begin
Count:=TStringList(Data).Count;
SetLength(Result, Count);
for I:=0 to Count-1 do
Result[I]:=TStringList(Data)[I];
end);
Result:=Mar.Marshal(Self) as TJSonObject;
finally
Mar.Free;
end;
end;

class function TTestObject.Unmarshal(value: TJSONObject): TTestObject;

var
Mar: TJSONUnMarshal;
L: TStringList;

begin
Mar := TJSONUnMarshal.Create();
try
Mar.RegisterReverter(TStringList,
function(Data: TListOfStrings): TObject

var
I, Count: Integer;
begin
Count := Length(Data);
Result:=TStringList.Create;
for I := 0 to Count - 1 do
TStringList(Result).Add(string(Data[I]));
end
);
//UnMarshal will attempt to create a TTestObject from the TJSONObject data
//using RTTI lookup - for that to function, the type MUST be defined in a unit
Result:=Mar.UnMarshal(Value) as TTestObject;
finally
Mar.Free;
end;
end;

end.

另请注意,构造函数已被重载 - 这使您可以看到代码可以正常运行,而无需在创建过程中预先填充对象中的数据。

这是通用类列表对象的实现

    unit uTestObjectList;

interface

uses
SysUtils, Classes, Contnrs, Generics.Defaults, Generics.Collections,
DbxJson, DbxJsonReflect, uTestObject;

type
{$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
TTestObjectList<T:TTestObject,constructor> = class(TObjectList<T>)
public
function Marshal: TJSonObject;
constructor Create;
class function Unmarshal(value: TJSONObject): TTestObjectList<T>; static;
end;

//Note: this MUST be present and initialized/finalized so that
//delphi will keep the RTTI information for the generic class available
//also, it MUST be "project global" - not "module global"
var
X:TTestObjectList<TTestObject>;

implementation

{ TTestObjectList<T> }
constructor TTestObjectList<T>.Create;
begin
inherited Create;
//removed the add for test data - it corrupts unmarshaling because the data is already present at creation
end;

function TTestObjectList<T>.Marshal: TJSonObject;
var
Marshal: TJsonMarshal;
begin
Marshal := TJSONMarshal.Create;
try
Marshal.RegisterConverter(TTestObjectList<T>,
function(Data: TObject): TListOfObjects
var
I: integer;

begin
SetLength(Result,TTestObjectlist<T>(Data).Count);
for I:=0 to TTestObjectlist<T>(Data).Count-1 do
Result[I]:=TTestObjectlist<T>(Data)[I];
end
);
Result := Marshal.Marshal(Self) as TJSONObject;
finally
Marshal.Free;
end;
end;

class function TTestObjectList<T>.Unmarshal(value: TJSONObject): TTestObjectList<T>;

var
Mar: TJSONUnMarshal;
L: TStringList;

begin
Mar := TJSONUnMarshal.Create();
try
Mar.RegisterReverter(TTestObjectList<T>,
function(Data: TListOfObjects): TObject
var
I, Count: Integer;
begin
Count := Length(Data);
Result:=TTestObjectList<T>.Create;
for I := 0 to Count - 1 do
TTestObjectList<T>(Result).Unmarshal(TJSONObject(Data[I]));
end
);
//UnMarshal will attempt to create a TTestObjectList<TTestObject> from the TJSONObject data
//using RTTI lookup - for that to function, the type MUST be defined in a unit,
//and, because it is generic, there must be a GLOBAL VARIABLE instantiated
//so that Delphi keeps the RTTI information avaialble
Result:=Mar.UnMarshal(Value) as TTestObjectList<T>;
finally
Mar.Free;
end;
end;


initialization
//force delphi RTTI into maintaining the Generic class information in memory
x:=TTestObjectList<TTestObject>.Create;

finalization
X.Free;

end.

有几件事需要注意:如果在运行时创建通用类,则不会保留 RTTI 信息,除非内存中有对该类的全局可访问对象引用。参见这里:Delphi: RTTI and TObjectList<TObject>

因此,上述单元创建了这样一个变量并使其实例化,如链接文章中所述。

主要过程已更新,显示了两个对象的数据编码和解码:

    procedure Main;
var
aTestobj,
bTestObj,
cTestObj : TTestObject;
aList,
bList : TTestObjectList<TTestObject>;
aJsonObject,
bJsonObject,
cJsonObject : TJsonObject;

s: string;

begin
aTestObj := TTestObject.Create(['one','two','three','four']);
aJsonObject := aTestObj.Marshal;
s:=aJsonObject.ToString;
Writeln(s);

bJsonObject:=TJsonObject.Create;
bJsonObject.Parse(BytesOf(s),0,length(s));

bTestObj:=TTestObject.Unmarshal(bJsonObject) as TTestObject;
writeln(bTestObj.List.Text);

writeln('TTestObject marshaling complete.');
readln;

aList := TTestObjectList<TTestObject>.Create;
aList.Add(TTestObject.Create(['one','two']));
aList.Add(TTestObject.Create(['three']));
aJsonObject := aList.Marshal;
s:=aJsonObject.ToString;
Writeln(s);

cJSonObject:=TJsonObject.Create;
cJSonObject.Parse(BytesOf(s),0,length(s));
bList:=TTestObjectList<TTestObject>.Unmarshal(cJSonObject) as TTestObjectList<TTestObject>;
for cTestObj in bList do
begin
writeln(cTestObj.List.Text);
end;

writeln('TTestObjectList<TTestObject> marshaling complete.');
Readln;
end;

关于delphi - 泛型和 Marshal/UnMarshal。我在这里缺少什么?第 2 部分 :-),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7670688/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com