泛型和元帅/ UnMarshal。我在这里错过了什么?第2部分 :-) [英] Generics and Marshal / UnMarshal. What am I missing here? PART #2 :-)

查看:178
本文介绍了泛型和元帅/ UnMarshal。我在这里错过了什么?第2部分 :-)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

继续我之前的问题:
泛型和元帅/ UnMarshal。在这里我缺少什么?



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



我觉得有必要再扩展这个问题。
所以我想听听大家如何做到这一点:首先 - 为了让源代码在XE2和XE2 update 1上运行,请进行以下更改: $ b

/ p>

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

为什么?
我能看到的必须与XE2相关的唯一原因是有更多的RTTI信息可用。因此它会尝试并将TObject归还。
我在正确的轨道上吗?请随时发表评论。



更重要 - 该示例没有实现UnMarshal方法。
如果任何人都可以制作一个并在此发布,我会喜欢: - )



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



亲切的问候
Bjarne

解决方案

除了这个答案问题,我已经在此处发布了一个解决方案,以解决您之前的问题:泛型和元帅/ UnMarshal。我缺少什么?



由于某些原因,使用TJsonobject的非默认构造函数会导致XE2中的问题 - 使用默认构造函数fixed 这个问题。

首先,你需要将你的TTestobject移动到它自己的单元中 - 否则,当试图解组时,RTTI将无法找到/创建你的对象。

  unit uTestObject; 

接口

使用
SysUtils,Classes,Contnrs,Generics.Defaults,Generics.Collections,DbxJson,DbxJsonReflect;

$ b $ $ {$ RTTI Expicit Methods([])PROPERTIES([vcPublished])FIELDS([vcPrivate])}
TTestObject = class(TObject)
private
list:TStringList;
public
构造函数创建;超载;
构造函数Create(list:string of array);超载;
构造函数Create(list:TStringList);超载;
析构函数Destroy;覆盖;
函数Marshal:TJSonObject;
类函数Unmarshal(value:TJSONObject):TTestObject;
发布
属性列表:TStringList读取aList写入aList;
end;

实现

{TTestObject}

构造函数TTestObject.Create;

begin
继承创建;
列表:= TStringList.Create;
end;

构造函数TTestObject.Create(list:string of array);

var
I:整数;

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

构造函数TTestObject.Create(list:TStringList);

begin
创建;
list.Assign(list);
end;

析构函数TTestObject.Destroy;

begin
列表。免费;
继承;
end;

函数TTestObject.Marshal:TJSonObject;

var
Mar:TJSONMarshal;

begin
Mar:= TJSONMarshal.Create();
尝试
Mar.RegisterConverter(TStringList,
函数(Data:TObject):TListOfStrings

var
I,Count:Integer;
开始
计数:= TStringList(Data).Count;
SetLength(Result,Count);
for I:= 0 to Count-1 do
结果[I]:= TStringList(Data)[I];
end);
结果:= Mar.Marshal(Self)as TJSonObject;
终于
Mar.Free;
end;
end;

类函数TTestObject.Unmarshal(value:TJSONObject):TTestObject;

var
Mar:TJSONUnMarshal;
L:TStringList;

begin
Mar:= TJSONUnMarshal.Create();
尝试
Mar.RegisterReverter(TStringList,
函数(Data:TListOfStrings):TObject

var
I,Count:Integer;
开始
计数:=长度(数据);
结果:= TStringList.Create;
for I:= 0 to Count - 1 do
TStringList(Result).Add(string (Data [I]));
end
);
// UnMarshal将尝试从TJSONObject数据
//使用RTTI查找创建一个TTestObject - 对于这个函数,类型必须在单元中定义
结果:= Mar.UnMarshal (Value)作为TTestObject;
终于
Mar.Free;
end;
end;

结束。

另请注意,构造函数已被重载 - 这使您可以看到代码没有预先 - 在创建过程中向对象中输入数据。



以下是泛型类列表对象的实现

  unit uTestObjectList; 

接口

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

类型
{$ RTTI显式方法([])PROPERTIES([])FIELDS([])}
TTestObjectList< T:TTestObject,constructor> = class(TObjectList< T>)
public
函数Marshal:TJSonObject;
构造函数创建;
class function Unmarshal(value:TJSONObject):TTestObjectList< T>;静态的;
end;

//注意:这个必须存在并初始化/最终化,以便
// delphi将保留泛型类可用的
//的RTTI信息,它必须是project global - 不是模块全局
var
X:TTestObjectList< TTestObject>;

实现

{TTestObjectList< T> }
构造函数TTestObjectList< T> .Create;
begin
继承创建;
//删除了测试数据的添加 - 它破坏了反编组,因为数据已经存在于创建
end;

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

开始
SetLength(Result,TTestObjectlist< T>(Data).Count);
for I:= 0 to TTestObjectlist< T>(Data).Count-1 do
Result [I ]:= TTestObjectlist< T>(Data)[I];
end
);
结果:= Marshal.Marshal(Self)as TJSONObject;
终于
Marshal.Free;
end;
end;

类函数TTestObjectList< T> .Unmarshal(value:TJSONObject):TTestObjectList< T>;

var
Mar:TJSONUnMarshal;
L:TStringList;

begin
Mar:= TJSONUnMarshal.Create();
尝试
Mar.RegisterReverter(TTestObjectList< T> ;,
函数(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将尝试创建一个TTestObjectList< TTestObject>从TJSONObject数据
//使用RTTI查找 - 对于这个函数,类型必须在单元中定义
//,并且因为它是通用的,所以必须有一个GLOBAL VARIABLE实例化的
//使得Delphi保持RTTI信息可用
结果:= Mar.UnMarshal(Value)as TTestObjectList< T>;
终于
Mar.Free;
end;
end;


初始化
//强制将delphi RTTI保存在内存中的Generic类信息
x:= TTestObjectList< TTestObject> .Create;

敲定
X.Free;

结束。

有以下几点值得注意:
如果在运行时,RTTI信息不会保留,除非在内存中存在对该类的全局可访问对象引用。请参阅:德尔福:RTTI和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;


Following up on my earlier question : Generics and Marshal / UnMarshal. What am I missing here?

In "part #1" (the link above) TOndrej provided a nice solution - that failed on XE2. Here I provide corrected source to correct that.

And I feel the need to expand this issue a bit more. So I would like to hear you all how to do this :

First - To get the source running on XE2 and XE2 update 1 make these changes :

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

Why ?? The only reason I can see must be related to XE2 is having a lot more RTTI information available. And hence it will try and marshal the TObject returned. Am I on the right track here? Please feel free to comment.

More important - the example does not implement an UnMarshal method. If anyone can produce one and post it here I would love it :-)

I hope that you still have interest in this subject.

Kind Regards Bjarne

解决方案

In addition to the answer to this question, I've posted a workaround to your previous question here: Generics and Marshal / UnMarshal. What am I missing here?

For some reason, using the non-default constructor of the TJsonobject causes the issue in XE2 - using the default constructor "fixed" the problem.

First, you need to move your TTestobject to its own unit - otherwise, RTTI won't be able to find/create your object when trying to unmarshal.

    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.

Also note that the constructor has been overloaded - this allows you to see that the code is functional without pre-pouplating the data in the object during creation.

Here is the implementation for the generic class list object

    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.

There are several things that are important to note: If a generic class is created at runtime, RTTI information is NOT kept unless there is a globally accessible object reference to that class in memory. See here: Delphi: RTTI and TObjectList<TObject>

So, the above unit creates such a variable and leaves it instantiated as discussed in the linked article.

The main procedure has been updated that shows both marshaling and unmarshaling the data for both objects:

    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;

这篇关于泛型和元帅/ UnMarshal。我在这里错过了什么?第2部分 :-)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆