2013-02-15 7 views
5

मेरे पास एक कनवर्टर है जिसे मैं केवल deserializing जब उपयोग करना चाहता हूँ। तो मैंने कैनव्राइट को झूठा करने के लिए सेट किया है, जो ठीक काम करता है और सब कुछ ठीक से serializes। जेसन स्ट्रिंग में तब एक ऑब्जेक्ट ग्राफ़ होता है जिसमें सांता क्लॉज आइटम की एक सरणी के साथ सांता क्लॉज चयन होता है और एक $ प्रकार इंगित करता है कि वे ठोस प्रकार सांता क्लॉज हैं।JSON.Net संग्रह आइटम के लिए CanConvert को कॉल नहीं कर रहा है?

हालांकि, जब यह deserializing के दौरान सांता क्लॉस के संग्रह से मुकाबला करता है, तो यह कभी भी कैनकॉनवर्ट नहीं कहता है (मेरे पास ब्रेक पॉइंट है और सांता क्लॉस कोलेक्शन देखें, जारी रखने के लिए F5 दबाएं, फिर उसमें किसी आइटम का सामना करते समय ब्रेक पॉइंट फिर से मारा जाना चाहिए सांता क्लॉस का संग्रह, लेकिन यह नहीं करता है)। जब यह सांता क्लाउस आइटम पर जाता है तो यह CanConvert को कॉल करने का प्रयास नहीं कर रहा है। उस कनवर्टर को यह भी जांचने के लिए कि क्या मेरा कनवर्टर इसे संभाल लेगा, यह जांचने के लिए कैनकॉन्टर को कॉल किए बिना, यह काम नहीं करेगा क्योंकि कक्षा में कोई डिफॉल्ट कन्स्ट्रक्टर नहीं है और संपत्ति-नाम मिलान सम्मेलनों के साथ कोई कन्स्ट्रक्टर नहीं है:

SantaClaus टाइप करने के लिए उपयोग करने के लिए एक कन्स्ट्रक्टर खोजने में असमर्थ। एक वर्ग या तो एक डिफ़ॉल्ट कन्स्ट्रक्टर होना चाहिए, तर्कों के साथ एक कन्स्ट्रक्टर या JsonConstructor विशेषता के साथ चिह्नित एक कन्स्ट्रक्टर होना चाहिए।

मुझे समझ में क्यों मैं इस त्रुटि मिलती है, लेकिन समस्या यह इंगित करता है कि Json.net, वस्तु deserialize करने की कोशिश की बजाय जाँच करें और देखें कि मेरी कनवर्टर बजाय अक्रमांकन को संभालने के लिए चाहता था CanConvert बुलाने की है।

संग्रह में प्रत्येक आइटम के लिए CanConvert क्यों नहीं कहा जा रहा है?

मेरे कनवर्टर:

class SantaClaus2JsonConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(SantaClaus);   
    } 

    /// <summary> 
    /// Deserializes a SantaClaus as a SantaClausEx which has a matching constructor that allows it to deserialize naturally. 
    /// </summary>  
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     return serializer.Deserialize<SantaClausEx>(reader); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     serializer.Serialize(writer, value); 
    } 


    public override bool CanRead 
    { 
     get 
     { 
      return true; 
     } 
    } 

    public override bool CanWrite 
    { 
     get 
     { 
      return false;//We only need this converter when reading. 
     } 
    } 

} 

SantaClausEx है सिर्फ एक नाम दिया पैरामीटर गुण मिलान करने के लिए के साथ एक निर्माता को जोड़ने के लिए Santaclaus से विरासत:

class SantaClaus //a third party class I can't modify 
{ 
    string Name {get;set;} 
    public SantaClaus(string santaClauseName) { this.Name = santaClauseName } 
} 

class SantaClausEx:SantaClaus 
{ 
    //provide a constructor with param names matching property names 
    public SantaClausEx(string name) : base(name) 
} 

Json.net एक Santaclaus deserialize नहीं कर सकते, लेकिन यह एक SantaClauseEx deserialize कर सकते हैं।

मैं हर जगह उस सांता क्लॉजएक्स कक्षा का उपयोग करता हूं और यह ठीक काम करता है, लेकिन मैं इसे स्वचालित रूप से करने के लिए कनवर्टर बनाना चाहता था।

इस JSON संग्रह के लिए कैसा दिखता है:

SantaClausCollection: [ 
{ 
    $type: "SantaClaus, XMasClasses.NET20" 
    Name: "St. Bob" 
}, 
{ 
    $type: "SantaClaus, XMasClasses.NET20" 
    Name: "St. Jim" 
} 
] 
+0

आप वास्तव में कन्वर्ट कॉल कैसे कर रहे हैं? Serialize/Deserialize विधियों में आम तौर पर ओवरलोड होते हैं जो लागू होने पर कनवर्टर्स का उपयोग करने के लिए स्वीकार करते हैं। आपका कनवर्टर जादुई रूप से सिर्फ इसलिए नहीं कहा जाएगा क्योंकि यह मौजूद है, इसे उन कन्वर्टर्स में से एक के रूप में पारित किया जाना चाहिए। –

उत्तर

1

मुझे लगता है कि आप Converters संग्रह करने के लिए अपने कनवर्टर जोड़ा सेटिंग्स वस्तु में देखें।

मैं कनवर्टर जो काम करता है

public class SantaClausJsonTest 
{ 
    public SantaClausJsonTest() 
    { 
     Settings = new JsonSerializerSettings(); 
     Settings.TypeNameHandling = TypeNameHandling.Objects; 
     Settings.Converters.Add(new SantaClaus2JsonConverter()); 
    } 

    private JsonSerializerSettings Settings; 

    [Fact] 
    public void SerializeAndDeserialize() 
    { 
     var collection = new [] 
      { 
       new SantaClaus("St. Bob"), 
       new SantaClaus("St. Jim"), 
      }; 

     var serialized = JsonConvert.SerializeObject(collection, Settings); 

     Console.WriteLine(serialized); 
     Assert.False(string.IsNullOrEmpty(serialized)); 

     var deserialized = JsonConvert.DeserializeObject<SantaClaus[]>(serialized, Settings); 

     Console.WriteLine(deserialized.GetType().ToString()); 
     Assert.NotNull(deserialized); 
     Assert.True(deserialized.Any(a => a.Name == "St. Bob")); 
     Assert.True(deserialized.Any(a => a.Name == "St. Jim")); 
    } 
} 

public class SantaClaus 
{ 
    public SantaClaus(string santaClauseName) 
    { 
     Name = santaClauseName; 
    } 

    public string Name { get; private set; } 
} 

public class SantaClaus2JsonConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(SantaClaus); 
    } 

    /// <summary> 
    /// Deserializes a SantaClaus as a SantaClausEx which has a matching constructor that allows it to deserialize naturally. 
    /// </summary>  
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     var name = string.Empty; 

     while (reader.Read()) 
     { 
      if (reader.TokenType == JsonToken.String && reader.Path.EndsWith("Name")) 
      { 
       name = reader.Value as string; 
      } 
      if (reader.TokenType == JsonToken.EndObject) 
      { 
       break; 
      } 
     } 

     return Activator.CreateInstance(objectType, name); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotSupportedException(); 
    } 


    public override bool CanRead 
    { 
     get 
     { 
      return true; 
     } 
    } 

    public override bool CanWrite 
    { 
     get 
     { 
      return false;//We only need this converter when reading. 
     } 
    } 
+0

परफेक्ट। कल तक बक्षीस देने का इंतजार करना है। – AaronLS

+3

सावधान रहें। ध्यान दें कि DeserializeObject । मैंने पाया है कि शीर्ष-स्तरीय JSON ऑब्जेक्ट्स के लिए, deserialization पर यह कनवर्टर को कॉल करने में विफल रहता है जब तक कि आप इस तरह के प्रकार को स्पष्ट रूप से हार्ड-कोड जेनेरिक 'DeserializeObjectमें करते हैं'विधि अधिभार। उदाहरण के लिए, यदि आपके पास JSON स्ट्रिंग थी जैसे '{" $ type ":" सांता क्लॉस "," नाम ":" anyname "} 'और DeserializeObject के साथ इसे deserialize करने की कोशिश की, यह एक सांता क्लॉज ऑब्जेक्ट का निर्माण करेगा, लेकिन आपके कनवर्टर के माध्यम से नहीं ReadJson विधि। यह सिर्फ प्रकार को तुरंत चालू करेगा और संपत्ति असाइन करेगा। एक बग, आईएमओ। – Triynko

+0

@Triynko अच्छा बिंदु। मुझे लगता है कि deserialize पर जेनेरिक प्रकार की कमी एक बात थी जो मैं अपने शुरुआती प्रयास के साथ लापता था। – AaronLS

1

मैं ने वही समस्या एक आधार वर्ग (कैसे आप एक SantaClauseEx वस्तु deserialize करने की आवश्यकता है के लिए इसी तरह से विरासत में मिली वस्तुओं deserializing था लेकिन वे सभी परिभाषित कर रहे हैं सांता क्लॉज ऑब्जेक्ट्स के रूप में)। मुद्दा जेएसओएन में है। उप प्रकार की पहचान करने में सक्षम नहीं है।

stackoverflow.com/questions/8030538/how-to-implement-custom-jsonconverter-in-json-net-to-deserialize-a-list-of-base

+0

धन्यवाद। "उप प्रकार की पहचान करने में सक्षम नहीं है", जेएसओएन में टाइप '$ प्रकार है:" सांता क्लाउस, 'जो ठीक है, क्योंकि मेरा कनवर्टर सांता क्लाउस' कैनकॉनवर्ट ... टाइपफ (सांता क्लाउस) 'के लिए है और मेरे कनवर्टर हैंडल इसे परिवर्तित कर रहा है SantaClausEx को। मैं Json.net को जादुई रूप से उप प्रकार का निर्धारण करने की उम्मीद नहीं कर रहा हूं। यह आदेश है कि Json.net CanConvert की जांच करने से पहले सांता क्लाउस का एक उदाहरण बनाने का प्रयास कर रहा है। इसलिए मेरा मानना ​​है कि आपके द्वारा लिंक किया गया समाधान वास्तव में काम करेगा समस्या के आसपास, क्योंकि मैं कनवर्टर का उपयोग करने के बजाय बेस टाइप के लिए निर्माण तर्क को कार्यान्वित कर सकता हूं, इसका परीक्षण करेगा। – AaronLS

0

एक बार मैं Rudus का जवाब काम कर रहे है, मैं अपने मूल प्रयास के साथ इस मुद्दे की पहचान के साथ साधारण परीक्षण लिखा था। जब आपके पास कोई डिफॉल्ट कंस्ट्रक्टर वाला कोई प्रकार नहीं है, तो वह बहुत अच्छा है, लेकिन संपत्ति मूल्यों को अपने अन्य रचनाकारों में से एक में मैप कर सकता है और मेरे विशिष्ट मामले के लिए निश्चित रूप से आसान है।

अगर किसी कारण से आपको वास्तव में ऐसा कुछ करने की ज़रूरत है जो मैं मूल रूप से करने की कोशिश कर रहा था, जहां आप एक अलग प्रकार बनाते हैं, जब मैं deserializing, मैं यह काम करने में सक्षम था।

public class SantaClaus2JsonConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(SantaClaus); 
    } 

    /// <summary> 
    /// Deserializes a SantaClaus as a SantaClausEx which has a matching constructor that allows it to deserialize naturally. 
    /// </summary>  
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     //temporarily switch off name handling so it ignores "SantaClaus" type when 
     //explicitely deserialize as SantaClausEx 
     //This could cause issues with nested types however in a more complicated object graph 
     var temp = serializer.TypeNameHandling; 
     serializer.TypeNameHandling = TypeNameHandling.None; 
     var desr = serializer.Deserialize<SantaClausEx>(reader); 
     serializer.TypeNameHandling = temp;//restore previous setting 

     return desr; 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { 
     throw new NotSupportedException(); 
    } 

    public override bool CanRead { get { return true; } } 

    public override bool CanWrite { get { false; } } //only for reading 

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