6

私は標準のデルファイのシリアル化を使用して標準的なデルファイのコンテナをシリアライズ/デシリアライズしようとしています。デシリアライズされた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>; 

が正しく辞書を作成しません。ここ はdictのコンストラクタによって作成どのように見えるかです: Dictionary deserialized wrong

私はそれを修正することができます方法: [Dictionary created correctly[1]

、ここでは、直列化復元によって作成された辞書のですか?

編集:ここでは は、問題がTJSONMarshalはRTTIを使用して辞書をインスタンス化していることである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

です。それは、それが見つけることができる最初のパラメータのないコンストラクタを呼び出すことによってそれを行います。そして、残念ながら、それは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

すばらしい答え!私はサブクラスで試しましたが、デシリアライザでエラーが発生しました:Internal:タイプMain.TDictionary をインスタンス化できません。 – aQuu

+0

そのユニットでRTTIが無効になっている可能性はありますか?ここから難しい。明らかに私が示したコードは明らかです。そして、私はその質問に答えたと思います。私たちはそれを見ることができないので、あなたの特定の文脈についてより詳細なアドバイスを与えることは非常に難しいです。少なくともあなたは何が起こっているのか理解しています。 –

+0

i TDictionary = class(TDictionary )をシリアライズしようとしましたが、エラーが発生しました。変更宣言の後:TDictionary = class(TDictionary )私は古いデルファイ(XE2)mayby thatsなぜ – aQuu

関連する問題