json – 为什么反序列化的TDictionary不能正常工作?

我尝试使用标准delphi序列化程序序列化/反序列化标准delphi容器.
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,反之亦然;

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>;

不能正确创建字典.
以下是构造函数创建dict的方式:
[

这是通过反序列化创建的dict:

我该如何解决

编辑:
这是JSON内容

{
       "type" : "System.Generics.Collections.TDictionary<system.integer,system.integer>","id" : 1,"fields" : {
          "FItems" : [
             [ -1,0 ],[ -1,[ 911574339,1,1 ]
          ],"FCount" : 1,"FGrowThreshold" : 3,"FKeyCollection" : null,"FValueCollection" : null
       }
    }

解决方法

问题是TJSONMarshal使用RTTI实例化字典.它通过调用它可以找到的第一个无参数构造函数来实现.而且,遗憾的是,这是TObject中定义的构造函数.

让我们看一下在TDictionary< K,V>中声明的构造函数.它们至少在我的XE7版本中:

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;

所有这些构造函数都有参数.

不要被你写的这个事实所迷惑

TDictionary<Integer,Integer>.Create

并创建一个分配了FComparer的实例.这解决了上面的第一个重载,因此编译器将该代码重写为

TDictionary<Integer,Integer>.Create(0)

填写认参数.

您需要做的是确保您只使用具有正确实例化类的无参数构造函数的类.不幸的是TDictionary< K,V>不符合要求.

但是,您可以派生一个引入无参数构造函数的子类,并且您的代码应该与该类一起使用.

以下代码演示:

{$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.

相关文章

前言 做过web项目开发的人对layer弹层组件肯定不陌生,作为l...
前言 前端表单校验是过滤无效数据、假数据、有毒数据的第一步...
前言 图片上传是web项目常见的需求,我基于之前的博客的代码...
前言 导出Excel文件这个功能,通常都是在后端实现返回前端一...
前言 众所周知,js是单线程的,从上往下,从左往右依次执行,...
前言 项目开发中,我们可能会碰到这样的需求:select标签,禁...