问题描述
我尝试使用标准delphi序列化程序对标准delphi容器进行序列化/反序列化。
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;
有一种方法可以将对象转换为JSON,反之亦然;
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;
行:
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:
我该如何解决?
编辑:
这是JSON内容
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
}
}
推荐答案
问题是 TJSONMarshal
使用以下实例化字典RTTI。它通过调用它可以找到的第一个无参数构造函数来实现。而且,可悲的是,这是在 TObject
中定义的构造函数。
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 in TObject
.
我们来看一下 TDictionary< K,V>
中声明的构造函数。它们至少在我的XE7版本中是:
Let's take a look at the constructors declared in TDictionary<K,V>
. They are, at least in my XE7 version:
constructor Create(ACapacity: Integer = 0); overload;
constructor Create(const AComparer: IEqualityComparer<TKey>); overload;
constructor Create(ACapacity: Integer; const AComparer: IEqualityComparer<TKey>); overload;
constructor Create(const Collection: TEnumerable<TPair<TKey,TValue>>); overload;
constructor Create(const Collection: TEnumerable<TPair<TKey,TValue>>;
const AComparer: IEqualityComparer<TKey>); overload;
所有这些构造函数都有参数。
All of these constructors have parameters.
不要被您写
TDictionary<Integer, Integer>.Create
并创建分配了 FComparer
的实例。可以解决上面的第一个重载,因此编译器将该代码重写为
and create an instance with FComparer
assigned. That resolves to the first overload above and so the compiler re-writes that code as
TDictionary<Integer, Integer>.Create(0)
填写默认参数。
您需要做的是确保仅使用具有正确实例化该类的无参数构造函数的类。不幸的是 TDictionary< K,V>
不符合要求。
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.
以下代码演示:
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Generics.Collections,
System.Rtti;
type
TDictionary<K,V> = class(System.Generics.Collections.TDictionary<K,V>)
public
constructor Create;
end;
{ TDictionary<K, V> }
constructor TDictionary<K, V>.Create;
begin
inherited Create(0);
end;
type
TInstance<T: class> = class
class function Create: T; static;
end;
class function TInstance<T>.Create: T;
// mimic the way that your JSON marshalling code instantiates objects
var
ctx: TRttiContext;
typ: TRttiType;
mtd: TRttiMethod;
cls: TClass;
begin
typ := ctx.GetType(TypeInfo(T));
for mtd in typ.GetMethods do begin
if mtd.HasExtendedInfo and mtd.IsConstructor then
begin
if Length(mtd.GetParameters) = 0 then
begin
cls := typ.AsInstance.MetaclassType;
Result := mtd.Invoke(cls, []).AsType<T>;
exit;
end;
end;
end;
Result := nil;
end;
var
Dict: TDictionary<Integer, Integer>;
begin
Dict := TInstance<TDictionary<Integer, Integer>>.Create;
Dict.Add(0, 0);
Writeln(BoolToStr(Dict.ContainsKey(0), True));
Readln;
end.
这篇关于为什么反序列化TDictionary无法正常工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!