为什么反序列化TDictionary无法正常工作?

编程入门 行业动态 更新时间:2024-10-10 21:24:17
本文介绍了为什么反序列化TDictionary无法正常工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

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

本文发布于:2023-11-12 13:08:54,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1581591.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:无法正常   序列化   工作   TDictionary

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!