Json.NET कन्वर्टर्स का उपयोग संपत्तियों को डीसर्विलाइज़ करने के लिए


88

मेरे पास एक वर्ग परिभाषा है जिसमें एक संपत्ति होती है जो एक इंटरफ़ेस लौटाती है।

public class Foo
{ 
    public int Number { get; set; }

    public ISomething Thing { get; set; }
}

Json.NET का उपयोग करके Foo वर्ग को क्रमबद्ध करने का प्रयास मुझे एक त्रुटि संदेश देता है, जैसे "ISomething 'का एक उदाहरण नहीं बना सकता। ISomething एक interface या abstract class हो सकता है।"

क्या एक Json.NET विशेषता या कनवर्टर है जो मुझे Somethingdeserialization के दौरान उपयोग करने के लिए एक ठोस वर्ग निर्दिष्ट करने देगा?


मेरा मानना ​​है कि आपको एक संपत्ति का नाम निर्दिष्ट करने की आवश्यकता है जो
ram

मेरे पास है। मैं C # 3.5 में पेश किए गए ऑटो-कार्यान्वित गुणों के लिए आशुलिपि का उपयोग कर रहा हूं। msdn.microsoft.com/en-us/library/bb384054.aspx
dthrasher

4
प्रकार नहीं है। मुझे लगता है कि राम सही है, आपको अभी भी एक संपत्ति के नाम की आवश्यकता है। मुझे पता है कि यह आपकी समस्या से संबंधित नहीं है, लेकिन आपकी टिप्पणी के बाद मुझे लगा कि मुझे .NET में कुछ नई सुविधा याद आ रही है, जिसने आपको एक नाम के बिना एक संपत्ति निर्दिष्ट करने की अनुमति दी है।
श्री मूस

जवाबों:


92

Json.NET के साथ आप जो कर सकते हैं उनमें से एक है:

var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;

JsonConvert.SerializeObject(entity, Formatting.Indented, settings);

TypeNameHandlingझंडा एक जोड़ देगा $typeJSON, जो Json.NET पता करने के लिए जो ठोस प्रकार इसे में वस्तु deserialize करने की जरूरत है की अनुमति देता है के लिए संपत्ति। यह आपको इंटरफ़ेस या अमूर्त आधार वर्ग को पूरा करते हुए एक वस्तु को डिस्क्राइब करने की अनुमति देता है।

हालाँकि, नकारात्मक पक्ष यह है कि यह बहुत Json.NET-specific है। यह $typeपूरी तरह से योग्य प्रकार होगा, इसलिए यदि आप इसे टाइप जानकारी के साथ क्रमबद्ध कर रहे हैं, तो deserializer को इसे समझने में सक्षम होने की आवश्यकता है।

दस्तावेज़ीकरण: Json.NET के साथ सीरियलाइज़ेशन सेटिंग्स


दिलचस्प। मुझे इसके साथ खेलना होगा। अच्छी टिप!
दशरथ

2
Newtonsoft.Json के लिए यह समान काम करता है, लेकिन संपत्ति "$ प्रकार" है
Jaap

यह बहुत आसान था!
शिम्मी वेइटहैंडलर

1
उपयोग करते समय यहां संभव सुरक्षा मुद्दों के लिए देखें TypeNameHandling। देखें Newtonsoft Json में सावधानी TypeNameHandling जानकारी के लिए।
dbc

मैं कल कन्वर्टर्स के साथ पागलों की तरह संघर्ष करता था, और यह बेहतर और बेहतर तरीका था, धन्यवाद !!!
होरोथेनिक

52

आप इसे JsonConverter वर्ग के उपयोग के माध्यम से प्राप्त कर सकते हैं। मान लीजिए कि आपके पास एक इंटरफ़ेस संपत्ति वाला एक वर्ग है;

public class Organisation {
  public string Name { get; set; }

  [JsonConverter(typeof(TycoonConverter))]
  public IPerson Owner { get; set; }
}

public interface IPerson {
  string Name { get; set; }
}

public class Tycoon : IPerson {
  public string Name { get; set; }
}

आपका JsonConverter अंतर्निहित संपत्ति को क्रमबद्ध और डी-सीरियल करने के लिए जिम्मेदार है;

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

  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  {
    return serializer.Deserialize<Tycoon>(reader);
  }

  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  {
    // Left as an exercise to the reader :)
    throw new NotImplementedException();
  }
}

जब आप Json.Net के माध्यम से किसी संगठन के साथ काम करते हैं तो मालिक की संपत्ति के लिए अंतर्निहित IPerson टाइप टाइकून का होगा।


बहुत अच्छा। मुझे कन्वर्टर को एक कोशिश देनी होगी।
dthrasher

4
टैग "[JsonConverter (टाइपोफ़ (टाइकूनकॉन्डर))]" अभी भी काम करेगा अगर यह इंटरफ़ेस की सूची में था?
Zwik

40

JSONConvert.SerializeObject () के साथ एक अनुकूलित JsonSerializerSettings ऑब्जेक्ट पास करने के बजाय TypeNameHandling.Objects विकल्प के साथ, जैसा कि पहले उल्लेख किया गया है, आप बस एक विशेषता के साथ विशिष्ट इंटरफ़ेस संपत्ति को चिह्नित कर सकते हैं ताकि उत्पन्न JSON "$ प्रकार" गुणों के साथ फूला हुआ न हो। हर वस्तु पर:

public class Foo
{
    public int Number { get; set; }

    // Add "$type" property containing type info of concrete class.
    [JsonProperty( TypeNameHandling = TypeNameHandling.Objects )]
    public ISomething { get; set; }
}

प्रतिभाशाली। धन्यवाद :)
डैरेन यंग

5
इंटरफेस या अमूर्त वर्गों के संग्रह के लिए संपत्ति "ItemTypeNameHandling" है। उदाहरण: [JsonProperty (ItemTypeNameHandling = TypeNameHandling.Auto)]
एंथोनी एफ

इसके लिए शुक्रिया!
ब्रूडर

23

तीसरे पक्ष के न्यूटनसॉफ्ट जोंस कन्वर्टर के सबसे हालिया संस्करण में आप एक कंक्रीट प्रकार के साथ एक अवरोधक संपत्ति सेट कर सकते हैं।

public class Foo
{ 
    public int Number { get; private set; }

    public ISomething IsSomething { get; private set; }

    public Foo(int number, Something concreteType)
    {
        Number = number;
        IsSomething = concreteType;
    }
}

जब तक कुछ घटता है तब तक यह काम करना चाहिए। JSON कनवर्टर का उपयोग करने के प्रयास के मामले में भी एक डिफ़ॉल्ट खाली कंस्ट्रक्टर न रखें, आपको इसे कंक्रीट प्रकार वाले कंस्ट्रक्टर का उपयोग करने के लिए मजबूर करना होगा।

पुनश्च। यह आपको अपने बसने वालों को निजी बनाने की भी अनुमति देता है।


6
यह छतों से चिल्लाया जाना चाहिए! सच है, यह ठोस कार्यान्वयन पर बाधाओं को जोड़ता है, लेकिन यह उन परिस्थितियों के लिए अन्य दृष्टिकोणों की तुलना में बहुत अधिक सीधा है जहां इसका उपयोग किया जा सकता है।
मार्क मेउर

3
क्या होगा यदि हमारे पास कई ठोस प्रकारों के साथ 1 से अधिक निर्माता हैं, तो क्या यह अभी भी पता चल जाएगा?
तेमन शिपाही

1
यह उत्तर सभी जटिल बकवास की तुलना में बहुत सुंदर है जो आपको अन्यथा करना होगा। यह स्वीकृत उत्तर होना चाहिए। मेरे मामले में एक चेतावनी, हालांकि, मुझे काम करने के लिए कंस्ट्रक्टर से पहले [JsonConstructor] जोड़ना पड़ा था .... मुझे संदेह है कि आपके कंक्रीट निर्माणकर्ताओं में से केवल एक का उपयोग करने से आपकी (4 साल पुरानी) समस्या का समाधान हो जाएगा। @Teomanshipahi
nacitar sevaht

@nacitarsevaht मैं वापस जा सकता हूं और अपनी समस्या अब ठीक कर सकता हूं :) वैसे भी मुझे याद नहीं है कि यह क्या था, लेकिन जब मैं फिर से देखता हूं तो यह कुछ मामलों के लिए एक अच्छा समाधान है।
तेमन शिपाही

हम इसका भी उपयोग करते हैं, लेकिन मैं ज्यादातर मामलों में कन्वर्ट को प्राथमिकता देता हूं क्योंकि कंस्ट्रक्टर को कंक्रीट प्रकार को युग्मित करना पहली जगह में संपत्ति के लिए एक इंटरफेस का उपयोग करने के उद्देश्य को हरा देता है!
गाबे

19

एक ही समस्या थी तो मैं अपने स्वयं के कनवर्टर के साथ आया था जो ज्ञात प्रकार के तर्क का उपयोग करता है।

public class JsonKnownTypeConverter : JsonConverter
{
    public IEnumerable<Type> KnownTypes { get; set; }

    public JsonKnownTypeConverter(IEnumerable<Type> knownTypes)
    {
        KnownTypes = knownTypes;
    }

    protected object Create(Type objectType, JObject jObject)
    {
        if (jObject["$type"] != null)
        {
            string typeName = jObject["$type"].ToString();
            return Activator.CreateInstance(KnownTypes.First(x =>typeName.Contains("."+x.Name+",")));
        }

        throw new InvalidOperationException("No supported type");
    }

    public override bool CanConvert(Type objectType)
    {
        if (KnownTypes == null)
            return false;

        return (objectType.IsInterface || objectType.IsAbstract) && KnownTypes.Any(objectType.IsAssignableFrom);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Load JObject from stream
        JObject jObject = JObject.Load(reader);
        // Create target object based on JObject
        var target = Create(objectType, jObject);
        // Populate the object properties
        serializer.Populate(jObject.CreateReader(), target);
        return target;
    }

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

मैंने deserializing और serializing के लिए दो विस्तार विधियों को परिभाषित किया:

public static class AltiJsonSerializer
{
    public static T DeserializeJson<T>(this string jsonString, IEnumerable<Type> knownTypes = null)
    {
        if (string.IsNullOrEmpty(jsonString))
            return default(T);

        return JsonConvert.DeserializeObject<T>(jsonString,
                new JsonSerializerSettings
                {
                    TypeNameHandling = TypeNameHandling.Auto, 
                    Converters = new List<JsonConverter>
                        (
                            new JsonConverter[]
                            {
                                new JsonKnownTypeConverter(knownTypes)
                            }
                        )
                }
            );
    }

    public static string SerializeJson(this object objectToSerialize)
    {
        return JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented,
        new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto});
    }
}

आप कन्वर्ट के प्रकारों की तुलना करने और पहचानने के अपने तरीके को परिभाषित कर सकते हैं, मैं केवल वर्ग नाम का उपयोग करता हूं।


1
यह JsonConverter बहुत अच्छा है, मैंने इसका इस्तेमाल किया, लेकिन मैंने इस तरह से हल करने वाले कुछ मुद्दों का सामना किया: - पॉप्युलेट करने के बजाय JsonSerializer.CreateDefault () का उपयोग करना, क्योंकि मेरी वस्तु में गहरा पदानुक्रम था। - कंस्ट्रक्टर को फिर से बनाने के लिए प्रतिबिंब का उपयोग करना और इसे बनाना () विधि में प्रयोग करना
Aurel

3

आम तौर पर मैंने हमेशा TypeNameHandlingडेनियलटी द्वारा सुझाए गए समाधान का उपयोग किया है , लेकिन यहां के मामलों में मैंने आने वाले JSON पर नियंत्रण नहीं किया है (और इसलिए यह सुनिश्चित नहीं कर सकता है कि इसमें एक $typeसंपत्ति शामिल है ) मैंने एक कस्टम कनवर्टर लिखा है जो आपको स्पष्ट रूप से निर्दिष्ट करने की अनुमति देता है ठोस प्रकार:

public class Model
{
    [JsonConverter(typeof(ConcreteTypeConverter<Something>))]
    public ISomething TheThing { get; set; }
}

यह सिर्फ Json.Net से डिफ़ॉल्ट धारावाहिक कार्यान्वयन का उपयोग करता है जबकि ठोस प्रकार को स्पष्ट रूप से निर्दिष्ट करता है।

स्रोत कोड और एक अवलोकन इस ब्लॉग पोस्ट पर उपलब्ध हैं ।


1
यह एक बेहतरीन उपाय है। चीयर्स।
जॉनमेटा

2

मैं सिर्फ उस उदाहरण को पूरा करना चाहता था जो @ डैनियल टी। ने हमें ऊपर दिखाया:

यदि आप अपनी वस्तु को क्रमबद्ध करने के लिए इस कोड का उपयोग कर रहे हैं:

var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;
JsonConvert.SerializeObject(entity, Formatting.Indented, settings);

इस कोड को इस तरह दिखना चाहिए:

var settings = new JsonSerializerSettings(); 
settings.TypeNameHandling = TypeNameHandling.Objects;
var entity = JsonConvert.DeserializeObject<EntityType>(json, settings);

TypeNameHandlingध्वज का उपयोग करने के दौरान यह बताया जाता है कि एक जसन कैसे अनुरूपित होता है :यहाँ छवि विवरण दर्ज करें


-5

मुझे आश्चर्य है कि यह वही बात है, लेकिन मुझे डर है कि यह नहीं किया जा सकता है।

आइए इसे इस तरह से देखें। आप JSon.net को डेटा की एक स्ट्रिंग, और एक प्रकार में डीज़ेरिअल करने के लिए सौंप देते हैं। जब यह हिट हो जाए तो JSON.net को क्या करना है? यह ISomething का एक नया प्रकार नहीं बना सकता क्योंकि ISomething एक वस्तु नहीं है। यह एक ऐसी वस्तु भी नहीं बना सकता है जो ISomething को कार्यान्वित करे, क्योंकि इसमें ऐसा कोई सुराग नहीं है जो कई वस्तुओं में से एक है जो ISomething को इनहेरिट कर सकता है। इंटरफेस, कुछ ऐसी चीजें हैं जो स्वचालित रूप से क्रमबद्ध हो सकती हैं, लेकिन स्वचालित रूप से deserialized नहीं।

एक बेस क्लास के साथ इसोमेलेस्ट की जगह पर मैं क्या देखूंगा। इसका उपयोग करके आप वह प्रभाव प्राप्त करने में सक्षम हो सकते हैं जिसकी आप तलाश कर रहे हैं।


1
मुझे लगता है कि यह "बॉक्स से बाहर" काम नहीं करेगा। लेकिन मैं सोच रहा था कि क्या "[JsonProperty (टाइपोफ़ (समरबेस))]" जैसे कुछ गुण हैं जो कि मैं एक ठोस वर्ग प्रदान करने के लिए उपयोग कर सकता हूं।
dthrasher

तो क्यों नहीं उपरोक्त कोड में ISomething के बजाय SomeBase का उपयोग करें? यह तर्क दिया जा सकता है कि हम इसे गलत तरीके से भी देख रहे हैं क्योंकि इंटरफेसेस को क्रमांकन में उपयोग नहीं किया जाना चाहिए, क्योंकि वे एक दिए गए वर्ग के साथ संचार "इंटरफ़ेस" को परिभाषित करते हैं। एक इंटरफेस को तकनीकी रूप से सीरियल करना बकवास है, जैसा कि एक अमूर्त वर्ग को क्रमबद्ध करना है। तो जबकि यह "किया जा सकता है" मैं तर्क देता हूँ कि यह "नहीं किया जाना चाहिए"।
टिमोथी बाल्ड्रिज

क्या आपने Newtonsoft.Json.Serialization Namespace में किसी भी वर्ग को देखा है? विशेष रूप से JsonObjectContract वर्ग?
जॉनी

-9

यहां स्कॉटगू द्वारा लिखे गए एक लेख का संदर्भ दिया गया है

उसके आधार पर, मैंने कुछ कोड लिखे जो मुझे लगता है कि सहायक हो सकते हैं

public interface IEducationalInstitute
{
    string Name
    {
        get; set;
    }

}

public class School : IEducationalInstitute
{
    private string name;
    #region IEducationalInstitute Members

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    #endregion
}

public class Student 
{
    public IEducationalInstitute LocalSchool { get; set; }

    public int ID { get; set; }
}

public static class JSONHelper
{
    public static string ToJSON(this object obj)
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        return serializer.Serialize(obj);
    }
    public  static string ToJSON(this object obj, int depth)
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        serializer.RecursionLimit = depth;
        return serializer.Serialize(obj);
    }
}

और यह है कि आप इसे कैसे कहेंगे

School myFavSchool = new School() { Name = "JFK High School" };
Student sam = new Student()
{
    ID = 1,
    LocalSchool = myFavSchool
};
string jSONstring = sam.ToJSON();

Console.WriteLine(jSONstring);
//Result {"LocalSchool":{"Name":"JFK High School"},"ID":1}

यदि मैं इसे सही ढंग से समझता हूं, तो मुझे नहीं लगता कि आपको एक ठोस वर्ग निर्दिष्ट करने की आवश्यकता है जो JSON क्रमांकन के लिए इंटरफ़ेस को लागू करता है।


1
आपका नमूना .NET फ्रेमवर्क में एक वर्ग JavaScriptSerializer का उपयोग करता है। मैं अपने धारावाहिक के रूप में Json.NET का उपयोग कर रहा हूं। codeplex.com/Json
dthrasher

3
मूल प्रश्न का संदर्भ नहीं है, Json.NET का स्पष्ट रूप से उल्लेख किया गया था।
ओलिवर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.