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

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

This question is now in Quality Central QC # 99313 , please vote for it :-)

As of 10-20-2011, Embarcadero marked the quality control report as RESOLVED. The solution was provided by SilverKnight. But I am concerned about the lack of information from Embarcadero. Because the solution suggests using a different source code than the one explained in the XE (2) help system, other forums, and CC. But look at 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 have to register the converter for TTestObject and for the beauty of it - call the 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); 

General Marshal TTestObjectList 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 a 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 recovery (unmarshal). But the code above should work - at least to my best knowledge.

So, if anyone can tell me:

Why doesn't the list marshal?

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

Going to TTypeStringConverter (instead of TTypeObjectConverter) will return a valid string. But I like the idea of ​​working on TJsonObject. Otherwise, I would have the same problem (or something similar) when doing unmarshalling from a string in TTestObject.

Any tips / ideas are welcome.

+3
source share
2 answers

I'm not sure why you got this error. For me, Delphi XE, the following works:

 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. 
+2
source

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

Please note that when creating the Marshal variable in the project, the following code is defined:

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

Changing both strings:

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

fixes the problem - the test application provided by TOndrej runs without errors.

+4
source

Source: https://habr.com/ru/post/907929/


All Articles