开发者

Generics and Marshal / UnMarshal. What am I missing here?

开发者 https://www.devze.com 2023-04-07 15:55 出处:网络
Better mention this : I\'m using Delphi XE2 - but XE or 2010 should do the trick too :-) This Question is now in Quality Central QC#99313 please vote it up :-)

Better mention this : I'm using Delphi XE2 - but XE or 2010 should do the trick too :-)

This Question is now in Quality Central QC#99313 please vote it up :-)

As of 10-20-2011 Embarcadero has marked the QC report as RESOLVED. Solution was provided by SilverKnight. But the lack of information from Embarcadero worries me. Since the solution suggests using other source code than the one explained in XE(2) Help system, other forums and CC. But have a look at the QC yourself.

Given these type declarations:

type
TTestObject : Class
    aList : TStringList;
    function Marshal : TJSonObject;
  end;

  TTestObjectList<T:TestObject> : Class(TObjectList<T>)
    function Marshal : TJSonObject; // How to write this ? 
  end;

I would like to implement a Marshal method for TTestObjectList. To my best knowledge - I should register a converter for TTestObject and for the beauty of it - call Marshal for each element.

The Marshal for TTestObject registers this converter:

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);

The Generic TTestObjectList Marshal method:

function TTestObjectList<T>.Marshal: TJSONObject;
var
Mar : TJsonMarshal;  // is actually a property on the list.
begin
  Mar := TJsonMarshal.Create(TJSONConverter.Create);
  try
    RegisterConverter(TTestObject,
      function(Data: TObject): TObject
      begin
        Result := TTestObject(Data).Marshal;
      end);
    Result := Mar.Marshal(Self) as TJSONObject;
  finally
    Mar.Free;
  end;
end;

Here is a simplified example of using the list.

var
  aTestobj : TTestObject; 
  aList : TTestObjectList<TTestObject>;
  aJsonObject : TJsonObject;
begin
  aTestObj := TTestObject.Create; // constructor creates and fills TStringlist with dummy data.
  aJsonObject := aTestObj.Marshal; // This works as intended.

  aList := TTestObjectList<TTestObject>.Create;
  aJsonObject := aList.Marshal; // Fails with tkpointer is unknown .... 
end;

Of course I have similar functionality for restoring (unmarshal). But the above code should work - at least to my best knowledge.

So if anyone can point out to me :

Why the List fails to marshal?

I know I have the TJsonMarshal property on my list - but it also has a converter/reverter.

Changing to a TTypeStringConverter (instead of TTypeObjectConverter) will r开发者_运维百科eturn a valid string. But I like the idea of working on a TJsonObject all along. Otherwise I would have the same problem (or something similar) when doing the unmarshalling from a string to TTestObject.

Any advice / ideas are most welcome.


Here is a "workaround" to "fix" the problem on Delphi XE2 (I was able to duplicate the same run time error).

Note that when Creating the Marshal variable that the following code is defined in the project:

Marshal := TJSONMarshal.Create(TJSONConverter.Create);

Changing BOTH lines to this:

Marshal := TJSONMarshal.Create;  //use the default constructor - which does the same thing as TJSONConvert.Create already

resolves the problem - the test application provided by TOndrej then executes without error.


I'm not sure why you get that error. The following seems to work for me, in Delphi XE:

program Project1;

{$APPTYPE CONSOLE}

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

type
  TTestObject = class(TObject)
    aList : TStringList;
    function Marshal : TJSonObject;
  public
    constructor Create;
    destructor Destroy; override;
  end;

  TTestObjectList<T:TTestObject,constructor> = class(TObjectList<T>)
    function Marshal: TJSonObject;
    constructor Create;
  end;

{ TTestObject }

constructor TTestObject.Create;
begin
  inherited Create;
  aList := TStringList.Create;
  aList.Add('one');
  aList.Add('two');
  aList.Add('three');
end;

destructor TTestObject.Destroy;
begin
  aList.Free;
  inherited;
end;

function TTestObject.Marshal: TJSonObject;
var
  Marshal: TJSONMarshal;
begin
  Marshal := TJSONMarshal.Create(TJSONConverter.Create);
  try
    Marshal.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 := Marshal.Marshal(Self) as TJSONObject;
  finally
    Marshal.Free;
  end;
end;

{ TTestObjectList<T> }

constructor TTestObjectList<T>.Create;
begin
  inherited Create;
  Add(T.Create);
  Add(T.Create);
end;

function TTestObjectList<T>.Marshal: TJSonObject;
var
  Marshal: TJsonMarshal;
begin
  Marshal := TJSONMarshal.Create(TJSONConverter.Create);
  try
    Marshal.RegisterConverter(TTestObject,
      function (Data: TObject): TObject
      begin
        Result := T(Data).Marshal;
      end
      );
    Result := Marshal.Marshal(Self) as TJSONObject;
  finally
    Marshal.Free;
  end;
end;

procedure Main;
var
  aTestobj : TTestObject;
  aList : TTestObjectList<TTestObject>;
  aJsonObject : TJsonObject;
begin
  aTestObj := TTestObject.Create;
  aJsonObject := aTestObj.Marshal;
  Writeln(aJsonObject.ToString);

  Writeln;
  aList := TTestObjectList<TTestObject>.Create;
  aJsonObject := aList.Marshal;
  Writeln(aJsonObject.ToString);

  Readln;
end;

begin
  try
    Main;
  except
    on E: Exception do
    begin
      ExitCode := 1;
      Writeln(Format('[%s] %s', [E.ClassName, E.Message]));
    end;
  end;
end.
0

精彩评论

暂无评论...
验证码 换一张
取 消