2017-02-10 78 views
6

我嘗試使用標準的delphi序列化器對標準的delphi容器進行序列化/反序列化。爲什麼反序列化的TDictionary不能正確工作?

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

不會產生字典正確。 這裏是如何看起來字典的構造函數創建: [Dictionary created correctly[1]

,這裏是由字典創建反序列化: Dictionary deserialized wrong

我怎樣才能解決呢?

編輯: 這裏是JSON內容

{ 
     "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 
     } 
    } 
+0

,你可以添加JSON的內容? – mjn

回答

9

的問題是,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. 
+0

很好的答案!我嘗試使用子類,但反序列化器引發錯誤:內部:無法實例化類型Main.TDictionary 。 – aQuu

+0

是否有可能在該單元中禁用了RTTI?很難從這裏說。很顯然,我展示的代碼是有效的。我想我已經回答了這個問題。因爲我們看不到它,所以很難給你提供關於你的特定環境的更詳細的建議。至少你明白髮生了什麼。 –

+0

我試過序列化TDictionary = class(TDictionary )但它引發錯誤。修改後聲明爲:TDictionary = class(TDictionary )正常工作!我有老的Delphi(XE2)mayby那就是爲什麼 – aQuu