为什么反序列化TDictionary无法正常工作? [英] Why does a deserialized TDictionary not work correctly?

查看:97
本文介绍了为什么反序列化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无法正常工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆