6

मैं मानक डेल्फी धारावाहिक का उपयोग कर मानक डेल्फी कंटेनर को क्रमबद्ध/deserialize करने का प्रयास करें।एक deserialized 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; 

एक तरीका है जिसे मैं ऑब्जेक्ट को जेएसओएन में परिवर्तित करता हूं और इसके विपरीत;

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 created correctly[1]

और यहाँ अक्रमांकन द्वारा बनाई dict है: Dictionary deserialized wrong

मैं इसे कैसे ठीक कर सकते हैं?

संपादित करें: यहाँ समस्या यह है कि TJSONMarshal शब्दकोश RTTI का उपयोग कर instantiating है 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

महान जवाब! मैंने सबक्लास के साथ प्रयास किया लेकिन deserializer त्रुटि बढ़ाएं: आंतरिक: Main.TDictionary को तत्काल नहीं कर सकता। – aQuu

+0

क्या यह संभव है कि आपके पास उस इकाई में आरटीटीआई अक्षम है? यहां से कहना मुश्किल है। स्पष्ट रूप से मैंने जो कोड दिखाया वह काम करता है। और मुझे लगता है कि मैंने सवाल का जवाब दिया है। आपको अपने विशेष संदर्भ पर अधिक विस्तृत सलाह देना बहुत मुश्किल है क्योंकि हम इसे नहीं देख सकते हैं। कम से कम आप समझते हैं कि क्या हो रहा है। –

+0

मैंने TDictionary = कक्षा (TDictionary ) क्रमबद्ध करने का प्रयास किया लेकिन यह त्रुटि उठाई। परिवर्तन के बाद घोषणा: TDictionary = वर्ग (TDictionary ) काम करता है! मेरे पास पुरानी डेल्फी (एक्सई 2) मैबी है क्यों – aQuu

संबंधित मुद्दे