I try serialize/deserialize standard delphi container using standard delphi serializer.
procedure TForm7.TestButtonClick(Sender: TObject);
var
dict: TDictionary<Integer, Integer>;
jsonValue: TJSONValue;
begin
//serialization
dict := TDictionary<Integer, Integer>.Create;
dict.Add(1, 1);
jsonValue := TJsonConverter.ObjectToJSON(dict);
dict.Free;
//deserialization
dict := TJsonConverter.JSONToObject(jsonValue) as TDictionary<Integer, Integer>;
try
Assert(dict.ContainsKey(1), 'deserialization error - key not found');
except
Assert(false, 'deserialization error - dict object broken');
end;
end;
There is a way I convert object to JSON and vice versa;
class function TJsonConverter.JSONToObject(AJSONValue: TJSONValue): TObject;
var
lUnMarshal: TJSONUnMarshal;
begin
lUnMarshal := TJSONUnMarshal.Create();
try
Result := lUnMarshal.Unmarshal(AJSONValue);
finally
lUnMarshal.Free;
end;
end;
class function TJsonConverter.ObjectToJSON(AData: TObject): TJSONValue;
var
lMarshal: TJSONMarshal;
begin
lMarshal := TJSONMarshal.Create();
try
Result := lMarshal.Marshal(AData);
finally
lMarshal.Free;
end;
end;
line:
dict := TJsonConverter.JSONToObject(jsonValue) as TDictionary<Integer, Integer>;
doesn't create dictionary correctly. Here is how looks dict create by constructor: [
and here is dict created by deserialization:
How can I fix it?
Edit: Here is JSON content
{
"type" : "System.Generics.Collections.TDictionary<System.Integer,System.Integer>",
"id" : 1,
"fields" : {
"FItems" : [
[ -1, 0, 0 ],
[ -1, 0, 0 ],
[ -1, 0, 0 ],
[ 911574339, 1, 1 ]
],
"FCount" : 1,
"FGrowThreshold" : 3,
"FKeyCollection" : null,
"FValueCollection" : null
}
}
The problem is that
TJSONMarshal
is instantiating the dictionary using RTTI. It does that by invoking the first parameterless constructor that it can find. And, sadly, that is the the constructor defined inTObject
.Let's take a look at the constructors declared in
TDictionary<K,V>
. They are, at least in my XE7 version:All of these constructors have parameters.
Don't be fooled by the fact that you write
and create an instance with
FComparer
assigned. That resolves to the first overload above and so the compiler re-writes that code asfilling in the default parameter.
What you need to do is make sure that you only use classes that have parameterless constructors that properly instantiate the class. Unfortunately
TDictionary<K,V>
does not fit the bill.You can however derive a sub-class that introduces a parameterless constructor, and your code should work with that class.
The following code demonstrates: