Дженерики и маршал / Унмаршал. Что мне здесь не хватает? - PullRequest
3 голосов
/ 23 сентября 2011

Лучше упомяните это: Я использую Delphi XE2 - но XE или 2010 тоже должны с этим справиться: -)

Этот вопрос сейчас в Quality Central QC # 99313 , пожалуйста, проголосуйте: -)

По состоянию на 10-20-2011 Embarcadero пометил отчет о КК как РАЗРЕШЕННЫЙ. Решение было предоставлено SilverKnight. Но отсутствие информации от Embarcadero беспокоит меня. Поскольку решение предлагает использовать другой исходный код, который не описан в справочной системе XE (2), других форумах и CC. Но посмотрите на КК самостоятельно.

С учетом этих объявлений типов:

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

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

Я хотел бы реализовать метод Marshal для TTestObjectList. Насколько мне известно - я должен зарегистрировать конвертер для TTestObject и для красоты об этом - вызвать маршала для каждого элемента.

Маршал для TTestObject регистрирует этот конвертер:

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

Общий метод маршала TTestObjectList:

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;

Вот упрощенный пример использования списка.

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;

Конечно, у меня есть аналогичные функции для восстановления (unmarshal). Но приведенный выше код должен работать - по крайней мере, насколько мне известно.

Так что, если кто-нибудь может указать мне:

Почему список не удается маршалировать?

Я знаю, что в моем списке есть свойство TJsonMarshal, но у него также есть конвертер / ревертер.

Изменение на TTypeStringConverter (вместо TTypeObjectConverter) вернет допустимую строку. Но мне нравится идея работать над TJsonObject все время. В противном случае у меня возникла бы такая же проблема (или что-то подобное) при выполнении демаршаллинга из строки в TTestObject.

Любые советы / идеи приветствуются.

Ответы [ 2 ]

4 голосов
/ 18 октября 2011

Вот «обходной путь» для «исправления» проблемы в Delphi XE2 (мне удалось воспроизвести ту же ошибку времени выполнения).

Обратите внимание, что при создании переменной Marshal определяется следующий кодв проекте:

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

Изменение ОБА строк на это:

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

решает проблему - тестовое приложение, предоставленное TOndrej, затем выполняется без ошибок.

2 голосов
/ 23 сентября 2011

Я не уверен, почему вы получаете эту ошибку.Мне кажется, в 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.
...