我尝试使用标准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无法正常工作?
发布评论