跟进我之前的问题: Generics and Marshal / UnMarshal. What am I missing here? 在“第1部分”(上面的链接)中,TOndrej提供了一个很好的解决方案 – 在XE2上失败了. 在这里,我提供纠正的来源来纠正这一
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信息.因此它将尝试并封送返回的TObject.
我在这里走在正确的轨道上吗?请随意发表评论.
更重要的是 – 该示例未实现UnMarshal方法.
如果有人可以制作一个并在此发布我会喜欢它:-)
我希望你仍然对这个问题感兴趣.
亲切的问候
比亚
出于某种原因,使用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;