JSON.net: डिफॉल्ट कंस्ट्रक्टर का उपयोग किए बिना कैसे डीसर्विलाइज़ करना है?


136

मेरे पास एक वर्ग है जिसमें एक डिफ़ॉल्ट रचनाकार है और एक अतिभारित रचनाकार भी है जो मापदंडों के एक सेट में लेता है। ये पैरामीटर ऑब्जेक्ट पर फ़ील्ड से मेल खाते हैं और निर्माण पर असाइन किए गए हैं। इस समय मुझे अन्य उद्देश्यों के लिए डिफ़ॉल्ट कंस्ट्रक्टर की आवश्यकता है, इसलिए यदि मैं कर सकता हूं तो मैं इसे रखना चाहूंगा।

मेरी समस्या: यदि मैं डिफ़ॉल्ट कंस्ट्रक्टर को हटाता हूं और JSON स्ट्रिंग में पास करता हूं, तो ऑब्जेक्ट सही ढंग से डिस्चार्ज हो जाता है और बिना किसी समस्या के कंस्ट्रक्टर पैरामीटर में पास हो जाता है। मैं अंत में उस वस्तु को वापस पा रहा हूं जिस तरह से मुझे उम्मीद थी। हालाँकि, जैसे ही मैं डिफॉल्ट कंस्ट्रक्टर को ऑब्जेक्ट में जोड़ता हूं, जब मैं कहता हूं JsonConvert.DeserializeObject<Result>(jsontext)कि प्रॉपर्टीज अब आबाद नहीं हैं।

इस बिंदु पर मैंने new JsonSerializerSettings(){CheckAdditionalContent = true}डिसेरिएलाइज़ेशन कॉल को जोड़ने की कोशिश की है । उसने कुछ नहीं किया।

एक और नोट। कंट्रक्टर पैरामीटर खेतों के नाम से बिल्कुल मेल खाते हैं सिवाय इसके कि पैरामीटर एक लोअरकेस अक्षर से शुरू होते हैं। मुझे नहीं लगता कि यह बात होगी, जैसा कि मैंने उल्लेख किया है, डिसेरिएलाइज़ेशन ठीक काम करता है जिसमें कोई डिफ़ॉल्ट कंस्ट्रक्टर नहीं है।

यहाँ मेरे रचनाकारों का एक नमूना है:

public Result() { }

public Result(int? code, string format, Dictionary<string, string> details = null)
{
    Code = code ?? ERROR_CODE;
    Format = format;

    if (details == null)
        Details = new Dictionary<string, string>();
    else
        Details = details;
}


जवाबों:


208

Json.Net किसी एक पर डिफ़ॉल्ट (पैरामीटर रहित) कंस्ट्रक्टर का उपयोग करना पसंद करता है यदि कोई है। यदि कई निर्माता हैं और आप चाहते हैं कि Json.Net एक गैर-डिफ़ॉल्ट का उपयोग करे, तो आप [JsonConstructor]उस निर्माणकर्ता के लिए विशेषता जोड़ सकते हैं जिसे आप Json.Net कॉल करना चाहते हैं।

[JsonConstructor]
public Result(int? code, string format, Dictionary<string, string> details = null)
{
    ...
}

यह महत्वपूर्ण है कि इसके लिए सही ढंग से काम करने के लिए कंस्ट्रक्टर पैरामीटर नाम JSON ऑब्जेक्ट (मामले की अनदेखी) के संबंधित संपत्ति नामों से मेल खाते हैं। हालांकि, जरूरी नहीं कि आपके पास हर प्रॉपर्टी के लिए एक कंस्ट्रक्टर पैरामीटर हो। उन JSON ऑब्जेक्ट प्रॉपर्टीज के लिए जिन्हें कंस्ट्रक्टर पैरामीटर द्वारा कवर नहीं किया गया है, Json.Net [JsonProperty]निर्माण के बाद ऑब्जेक्ट को पॉप्युलेट करने के लिए पब्लिक प्रॉपर्टी एक्सेसर्स (या प्रॉपर्टीज़ / फ़ील्ड्स से चिह्नित ) का उपयोग करने का प्रयास करेगा ।

यदि आप अपनी कक्षा में विशेषताओं को जोड़ना नहीं चाहते हैं या अन्यथा जिस कक्षा के लिए आप डीसर्विलाइज़ करने का प्रयास कर रहे हैं, उसके लिए स्रोत कोड को नियंत्रित नहीं करते हैं, तो एक दूसरा विकल्प कस्टम JsonConverter बनाने और अपनी वस्तु को पॉप्युलेट करने के लिए है। उदाहरण के लिए:

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Load the JSON for the Result into a JObject
        JObject jo = JObject.Load(reader);

        // Read the properties which will be used as constructor parameters
        int? code = (int?)jo["Code"];
        string format = (string)jo["Format"];

        // Construct the Result object using the non-default constructor
        Result result = new Result(code, format);

        // (If anything else needs to be populated on the result object, do that here)

        // Return the result
        return result;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

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

फिर, अपने सीरलाइज़र सेटिंग्स में कनवर्टर जोड़ें, और जब आप डिस्क्रिअलाइज़ करें तो सेटिंग्स का उपयोग करें:

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new ResultConverter());
Result result = JsonConvert.DeserializeObject<Result>(jsontext, settings);

4
यह काम किया। यह इस तरह बेकार है कि मुझे अब अपने मॉडल प्रोजेक्ट में JSON.net निर्भरता लेनी है, लेकिन क्या। मैं इसे उत्तर के रूप में चिह्नित करूंगा।
किमीकोल्डाल्ड

3
अन्य विकल्प हैं- आप JsonConverterअपनी कक्षा के लिए एक कस्टम बना सकते हैं । यह निर्भरता को हटा देगा, लेकिन फिर आपको कनवर्टर में ऑब्जेक्ट को तुरंत और पॉप्युलेट करना होगा। एक कस्टम लिखना भी संभव हो सकता है ContractResolverजो Json.Net को निर्देश देता है कि वह दूसरे कंस्ट्रक्टर का उपयोग करके इसे बदल JsonObjectContractसकता है, लेकिन यह सुनने में थोड़ा पेचीदा साबित हो सकता है।
ब्रायन रोजर्स

हाँ, मुझे लगता है कि विशेषता ठीक काम करेगी। डिसेरिएलाइज़ कॉल वास्तव में सामान्य है ताकि यह किसी भी प्रकार की वस्तु हो सके। मुझे लगता है कि आपका मूल उत्तर ठीक काम करेगा। जानकारी के लिए धन्यवाद!
किमीकैल्डाल्ड

2
यह वास्तव में मदद करेगा अगर निर्माण चयन के लिए एक और सम्मेलन निर्धारित करना संभव था। उदाहरण के लिए, मुझे लगता है कि यूनिटी कंटेनर इसका समर्थन करता है। तब आप इसे बना सकते हैं ताकि यह हमेशा डिफ़ॉल्ट एक पर वापस गिरने के बजाय अधिकांश मापदंडों के साथ कंस्ट्रक्टर को चुने। किसी भी संभावना इस तरह के एक विस्तार बिंदु Json.Net में मौजूद है?
जूलियागॉन

1
मत भूलनाusing Newtonsoft.Json;
ब्रूनो बीरी

36

थोड़ी देर और यहाँ बिल्कुल अनुकूल नहीं है, लेकिन मैं अपना समाधान यहाँ जोड़ने जा रहा हूँ, क्योंकि मेरा प्रश्न इस एक के डुप्लिकेट के रूप में बंद हो गया था, और क्योंकि यह समाधान पूरी तरह से अलग है।

मुझे Json.NETउपयोगकर्ता परिभाषित संरचना प्रकार के लिए सबसे विशिष्ट निर्माणकर्ता को प्राथमिकता देने का निर्देश देने के लिए एक सामान्य तरीके की आवश्यकता थी , इसलिए मैं उन JsonConstructorविशेषताओं को छोड़ सकता हूं जो उस परियोजना के लिए एक निर्भरता जोड़ देगा जहां इस तरह के प्रत्येक संरचना को परिभाषित किया गया है।

मैंने थोड़ा सा रिवर्स किया है और एक कस्टम कॉन्ट्रैक्ट रिज़ॉल्वर लागू किया है, जहाँ मैंने CreateObjectContractअपने कस्टम क्रिएशन लॉजिक को जोड़ने की विधि को ओवरराइड किया है ।

public class CustomContractResolver : DefaultContractResolver {

    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var c = base.CreateObjectContract(objectType);
        if (!IsCustomStruct(objectType)) return c;

        IList<ConstructorInfo> list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList();
        var mostSpecific = list.LastOrDefault();
        if (mostSpecific != null)
        {
            c.OverrideCreator = CreateParameterizedConstructor(mostSpecific);
            c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties));
        }

        return c;
    }

    protected virtual bool IsCustomStruct(Type objectType)
    {
        return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System.");
    }

    private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
    {
        method.ThrowIfNull("method");
        var c = method as ConstructorInfo;
        if (c != null)
            return a => c.Invoke(a);
        return a => method.Invoke(null, a);
    }
}

मैं इसे इस तरह उपयोग कर रहा हूं।

public struct Test {
  public readonly int A;
  public readonly string B;

  public Test(int a, string b) {
    A = a;
    B = b;
  }
}

var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings {
  ContractResolver = new CustomContractResolver()
});
var t = JsonConvert.DeserializeObject<Test>(json);
t.A.ShouldEqual(1);
t.B.ShouldEqual("Test");

2
मैं वर्तमान में ऊपर दिए गए स्वीकृत उत्तर का उपयोग कर रहा हूं, लेकिन साथ ही अपना समाधान दिखाने के लिए धन्यवाद देना चाहता हूं!
डॉटबर्ट

1
मैंने स्ट्रक्चर्स (चेक फॉर objectType.IsValueType) पर प्रतिबंध हटा दिया और यह बहुत अच्छा काम करता है, धन्यवाद!
एलेक्स एंगस

@AlexAngas हां सामान्य रूप से इस रणनीति को लागू करने से आपकी प्रतिक्रिया के लिए धन्यवाद होता है।
ज़ोल्टन तामसी

3

यहाँ कुछ उत्तरों के आधार पर, मैंने CustomConstructorResolverएक वर्तमान परियोजना में उपयोग के लिए लिखा है , और मुझे लगा कि यह किसी और की मदद कर सकता है।

यह निम्नलिखित संकल्प तंत्र का समर्थन करता है, सभी विन्यास योग्य:

  • एकल निजी कंस्ट्रक्टर का चयन करें ताकि आप एक निजी कंस्ट्रक्टर को बिना किसी विशेषता के चिह्नित कर सकें।
  • सबसे विशिष्ट निजी कंस्ट्रक्टर का चयन करें ताकि आपके पास कई ओवरलोड हो सकें, फिर भी विशेषताओं का उपयोग किए बिना।
  • किसी विशिष्ट नाम की विशेषता के साथ चिह्नित निर्माता का चयन करें - डिफ़ॉल्ट रिज़ॉल्वर की तरह, लेकिन Json.Net पैकेज पर निर्भरता के बिना क्योंकि आपको संदर्भ की आवश्यकता है Newtonsoft.Json.JsonConstructorAttribute
public class CustomConstructorResolver : DefaultContractResolver
{
    public string ConstructorAttributeName { get; set; } = "JsonConstructorAttribute";
    public bool IgnoreAttributeConstructor { get; set; } = false;
    public bool IgnoreSinglePrivateConstructor { get; set; } = false;
    public bool IgnoreMostSpecificConstructor { get; set; } = false;

    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var contract = base.CreateObjectContract(objectType);

        // Use default contract for non-object types.
        if (objectType.IsPrimitive || objectType.IsEnum) return contract;

        // Look for constructor with attribute first, then single private, then most specific.
        var overrideConstructor = 
               (this.IgnoreAttributeConstructor ? null : GetAttributeConstructor(objectType)) 
            ?? (this.IgnoreSinglePrivateConstructor ? null : GetSinglePrivateConstructor(objectType)) 
            ?? (this.IgnoreMostSpecificConstructor ? null : GetMostSpecificConstructor(objectType));

        // Set override constructor if found, otherwise use default contract.
        if (overrideConstructor != null)
        {
            SetOverrideCreator(contract, overrideConstructor);
        }

        return contract;
    }

    private void SetOverrideCreator(JsonObjectContract contract, ConstructorInfo attributeConstructor)
    {
        contract.OverrideCreator = CreateParameterizedConstructor(attributeConstructor);
        contract.CreatorParameters.Clear();
        foreach (var constructorParameter in base.CreateConstructorParameters(attributeConstructor, contract.Properties))
        {
            contract.CreatorParameters.Add(constructorParameter);
        }
    }

    private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
    {
        var c = method as ConstructorInfo;
        if (c != null)
            return a => c.Invoke(a);
        return a => method.Invoke(null, a);
    }

    protected virtual ConstructorInfo GetAttributeConstructor(Type objectType)
    {
        var constructors = objectType
            .GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
            .Where(c => c.GetCustomAttributes().Any(a => a.GetType().Name == this.ConstructorAttributeName)).ToList();

        if (constructors.Count == 1) return constructors[0];
        if (constructors.Count > 1)
            throw new JsonException($"Multiple constructors with a {this.ConstructorAttributeName}.");

        return null;
    }

    protected virtual ConstructorInfo GetSinglePrivateConstructor(Type objectType)
    {
        var constructors = objectType
            .GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);

        return constructors.Length == 1 ? constructors[0] : null;
    }

    protected virtual ConstructorInfo GetMostSpecificConstructor(Type objectType)
    {
        var constructors = objectType
            .GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
            .OrderBy(e => e.GetParameters().Length);

        var mostSpecific = constructors.LastOrDefault();
        return mostSpecific;
    }
}

यहाँ XML दस्तावेज़ के साथ एक जिस्ट के रूप में पूर्ण संस्करण दिया गया है: https://gist.github.com/maverickelementalch/80f77f4b6bdce3b434b0f7a1d06baa95

प्रतिक्रिया की सराहना की।


महान समाधान! साझा करने के लिए धन्यवाद।
थोमई

1

Newtonsoft.Json का डिफ़ॉल्ट व्यवहार निर्माणकर्ताओं को खोजने जा रहा है public। यदि आपका डिफ़ॉल्ट कंस्ट्रक्टर केवल क्लास या समान असेंबली में उपयोग किया जाता है, तो आप एक्सेस स्तर को कम कर सकते हैं protectedया internalऐसा कर सकते हैं कि Newtonsoft.Json आपके इच्छित publicकंस्ट्रक्टर को चुन ले ।

जाहिर है, यह समाधान विशिष्ट मामलों तक ही सीमित है।

internal Result() { }

public Result(int? code, string format, Dictionary<string, string> details = null)
{
    Code = code ?? ERROR_CODE;
    Format = format;

    if (details == null)
        Details = new Dictionary<string, string>();
    else
        Details = details;
}

-1

उपाय:

public Response Get(string jsonData) {
    var json = JsonConvert.DeserializeObject<modelname>(jsonData);
    var data = StoredProcedure.procedureName(json.Parameter, json.Parameter, json.Parameter, json.Parameter);
    return data;
}

नमूना:

public class modelname {
    public long parameter{ get; set; }
    public int parameter{ get; set; }
    public int parameter{ get; set; }
    public string parameter{ get; set; }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.