JSON.NET में deserialization के लिए कास्टिंग इंटरफेस


128

मैं एक पाठक को स्थापित करने की कोशिश कर रहा हूं जो विभिन्न वेबसाइटों से JSON ऑब्जेक्ट्स में ले जाएगा (सूचना स्क्रैपिंग के बारे में सोचें) और उन्हें C # ऑब्जेक्ट्स में अनुवाद करें। मैं वर्तमान में deserialization प्रक्रिया के लिए JSON.NET का उपयोग कर रहा हूं। मैं जिस समस्या में चल रहा हूं, वह यह है कि यह नहीं पता कि क्लास में इंटरफ़ेस-स्तरीय गुणों को कैसे संभालना है। तो प्रकृति का कुछ:

public IThingy Thing

त्रुटि उत्पन्न करेगा:

एक प्रकार की आईहिंगी का उदाहरण नहीं बना सके। प्रकार एक इंटरफ़ेस या अमूर्त वर्ग है और इसे तत्काल नहीं किया जा सकता है।

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

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

साथ ही, अगर यह मायने रखता है, तो मेरा ऐप .NET 4.0 में लिखा गया है।


जवाबों:


115

@SamualDavis ने संबंधित प्रश्न में एक महान समाधान प्रदान किया , जिसे मैं यहां संक्षेप में बताऊंगा ।

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

यहाँ एक उदाहरण है:

public class Visit : IVisit
{
    /// <summary>
    /// This constructor is required for the JSON deserializer to be able
    /// to identify concrete classes to use when deserializing the interface properties.
    /// </summary>
    public Visit(MyLocation location, Guest guest)
    {
        Location = location;
        Guest = guest;
    }
    public long VisitId { get; set; }
    public ILocation Location { get;  set; }
    public DateTime VisitDate { get; set; }
    public IGuest Guest { get; set; }
}

15
यह एक ICollection के साथ कैसे काम करेगा? ICollection <IGuest> मेहमान {प्राप्त; सेट;}
DrSammyD

12
यह ICollection <कंक्रीटक्लास> के साथ काम करता है, इसलिए ICollection <Guest> काम करता है। एक FYI के रूप में, आप अपने कंस्ट्रक्टर पर विशेषता [JsonConstructor] डाल सकते हैं ताकि यह डिफ़ॉल्ट रूप से उपयोग करें यदि आपके पास कई निर्माता हैं
DrSammyD

6
मैं एक ही समस्या से जुड़ा हुआ हूं, मेरे मामले में मेरे पास इंटरफ़ेस के कई कार्यान्वयन हैं (आपके उदाहरण में इंटरफ़ेस ILocation है) तो क्या होगा यदि MyLocation, VIPLocation, OrdinaryLocation जैसी कक्षाएं हैं। लोकेशन प्रॉपर्टी में ये मैप कैसे करें? यदि आपके पास सिर्फ एक कार्यान्वयन है जैसे MyLocation इसका आसान है, लेकिन यदि ILocation के कई कार्यान्वयन हैं तो यह कैसे करें?
23

10
यदि आपके पास एक से अधिक निर्माता हैं, तो आप [JsonConstructor]विशेषता के साथ अपने विशेष निर्माता को चिह्नित कर सकते हैं ।
डॉ। रॉब लैंग

26
यह बिल्कुल भी ठीक नहीं है। इंटरफेस का उपयोग करने का बिंदु निर्भरता इंजेक्शन का उपयोग करना है, लेकिन आपके निर्माता द्वारा आवश्यक ऑब्जेक्ट टाइप किए गए पैरामीटर के साथ ऐसा करने से आप संपत्ति के रूप में इंटरफ़ेस होने के बिंदु को पूरी तरह से पेंच कर देते हैं।
Jérôme MEVEL

57

( इस सवाल से नकल )

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

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

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

इस ब्लॉग पोस्ट पर एक अवलोकन उपलब्ध है । स्रोत कोड नीचे है:

public class ConcreteTypeConverter<TConcrete> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        //assume we can convert to anything for now
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        //explicitly specify the concrete type we want to create
        return serializer.Deserialize<TConcrete>(reader);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        //use the default serialization - it works fine
        serializer.Serialize(writer, value);
    }
}

11
मैं वास्तव में इस दृष्टिकोण को पसंद करता हूं और इसे हमारे अपने प्रोजेक्ट पर लागू करता हूं। मैंने एक ConcreteListTypeConverter<TInterface, TImplementation>प्रकार के वर्ग के सदस्यों को संभालने के लिए भी जोड़ा IList<TInterface>
ओलिवर

3
यह एक महान कोड है। यह concreteTypeConverterप्रश्न में वास्तविक कोड के लिए अच्छा हो सकता है ।
क्रिस

2
@ ओलिवर - क्या आप अपना ConcreteListTypeConverter<TInterface, TImplementation>कार्यान्वयन पोस्ट कर सकते हैं ?
माइकल

2
और अगर आपके पास ISomething के दो कार्यान्वयनकर्ता हैं?
bdaniel7

56

एक कनवर्टर का उपयोग क्यों करें? Newtonsoft.Jsonइस सटीक समस्या को हल करने के लिए एक देशी कार्यक्षमता है:

सेट TypeNameHandlingमें JsonSerializerSettingsकरने के लिएTypeNameHandling.Auto

JsonConvert.SerializeObject(
  toSerialize,
  new JsonSerializerSettings()
  {
    TypeNameHandling = TypeNameHandling.Auto
  });

यह हर प्रकार को जसन में डाल देगा, जिसे एक प्रकार के ठोस उदाहरण के रूप में नहीं बल्कि एक इंटरफ़ेस या एक सार वर्ग के रूप में रखा जाता है।

सुनिश्चित करें कि आप सीरियलाइज़ेशन और डिसिएरलाइज़ेशन के लिए समान सेटिंग्स का उपयोग कर रहे हैं ।

मैंने इसका परीक्षण किया, और यह सूचियों के साथ भी एक आकर्षण की तरह काम करता है।

खोज परिणाम वेब लिंक साइट लिंक के साथ

AR चेतावनी :

केवल एक ज्ञात और विश्वसनीय स्रोत से json के लिए इसका उपयोग करें। उपयोगकर्ता snipsnipsnip ने सही उल्लेख किया कि यह वास्तव में एक व्यवहार्यता है।

देखें CA2328 और SCS0028 अधिक जानकारी के लिए।


स्रोत और एक वैकल्पिक मैनुअल कार्यान्वयन: कोड इनसाइड ब्लॉग


3
बिल्कुल सही, इससे मुझे एक त्वरित और गंदे गहरे क्लोन ( stackoverflow.com/questions/78536/deep-cloning-objects ) के लिए मदद मिली
Compufreak

1
@ शमी ऑब्जेक्ट्स: "JSON ऑब्जेक्ट संरचना में अनुक्रमित करते समय .NET टाइप नाम शामिल करें।" ऑटो: .NET टाइप नाम शामिल करें जब ऑब्जेक्ट का प्रकार क्रमबद्ध किया जा रहा है तो उसके घोषित प्रकार के समान नहीं है। ध्यान दें कि इसमें मूल रूप से क्रमबद्ध ऑब्जेक्ट शामिल नहीं है। । जड़ वस्तु की प्रकार का नाम JSON में आप SerializeObject (वस्तु, प्रकार, JsonSerializerSettings) या क्रमानुसार (JsonWriter, वस्तु, प्रकार) "स्रोत के साथ एक रूट प्रकार वस्तु का उल्लेख करना होगा शामिल करने के लिए: newtonsoft.com/json/help/html/...
माफिया

4
मैं बस यह Deserialization पर कोशिश की और यह काम नहीं करता है। इस स्टैक ओवरफ्लो प्रश्न की विषय पंक्ति है, "JSON.NET में deserialization के लिए कास्टिंग इंटरफेस"
जस्टिन रसो

3
@JustinRusso यह केवल तभी काम करता है जब एक ही सेटिंग के साथ
जसन

3
जल्दी के लिए अपवित्र, यदि गंदा नहीं है, तो समाधान। यदि आप केवल कॉन्फ़िगरेशन को क्रमबद्ध कर रहे हैं, तो यह काम करता है। कन्वर्टर्स के निर्माण के लिए विकास को रोकते हुए धड़कता है और निश्चित रूप से हर इंजेक्शन संपत्ति को सजाता है। serializer.TypeNameHandling = TypeNameHandling.Auto; JsonConvert.DefaultSettings ()। टाइपनेमहैंडलिंग = टाइपनामहैंडलिंग.आटो;
सीन एंडरसन

39

इंटरफेस के कई कार्यान्वयन के deserialization को सक्षम करने के लिए, आप JsonConverter का उपयोग कर सकते हैं, लेकिन एक विशेषता के माध्यम से नहीं:

Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Converters.Add(new DTOJsonConverter());
Interfaces.IEntity entity = serializer.Deserialize(jsonReader);

DTOJsonConverter एक कंक्रीट कार्यान्वयन के साथ प्रत्येक इंटरफ़ेस को मैप करता है:

class DTOJsonConverter : Newtonsoft.Json.JsonConverter
{
    private static readonly string ISCALAR_FULLNAME = typeof(Interfaces.IScalar).FullName;
    private static readonly string IENTITY_FULLNAME = typeof(Interfaces.IEntity).FullName;


    public override bool CanConvert(Type objectType)
    {
        if (objectType.FullName == ISCALAR_FULLNAME
            || objectType.FullName == IENTITY_FULLNAME)
        {
            return true;
        }
        return false;
    }

    public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
    {
        if (objectType.FullName == ISCALAR_FULLNAME)
            return serializer.Deserialize(reader, typeof(DTO.ClientScalar));
        else if (objectType.FullName == IENTITY_FULLNAME)
            return serializer.Deserialize(reader, typeof(DTO.ClientEntity));

        throw new NotSupportedException(string.Format("Type {0} unexpected.", objectType));
    }

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

DTOJsonConverter केवल डेज़राइज़र के लिए आवश्यक है। क्रमांकन प्रक्रिया अपरिवर्तित है। JSON ऑब्जेक्ट को ठोस प्रकार के नामों को एम्बेड करने की आवश्यकता नहीं है।

यह एसओ पोस्ट एक समान समाधान प्रदान करता है एक सामान्य JsonConverter के साथ।


नहीं है कि WriteJson पद्धति के serializer करने के लिए कॉल करें। पुष्टि करें कि स्टैक अतिप्रवाह का कारण बनता है, क्योंकि कनवर्टर द्वारा क्रमबद्ध किए जा रहे मूल्य पर क्रमबद्ध कॉल करने से कनवर्टर के WriteJson विधि को फिर से पुनरावर्ती कहा जाएगा?
त्रिनको डे

यह नहीं होना चाहिए, यदि CanConvert () विधि एक सुसंगत परिणाम देता है।
एरिक ब्यूमेन्डिल

3
FullNameजब आप सीधे प्रकारों की तुलना कर रहे हैं तो आप एस की तुलना क्यों कर रहे हैं ?
एलेक्स

बस तुलना प्रकार भी ठीक है।
एरिक बाउमेंडिल

23

अमूर्त प्रकार को वास्तविक प्रकार में मैप करने के लिए इस वर्ग का उपयोग करें:

public class AbstractConverter<TReal, TAbstract> : JsonConverter where TReal : TAbstract
{
    public override Boolean CanConvert(Type objectType) 
        => objectType == typeof(TAbstract);

    public override Object ReadJson(JsonReader reader, Type type, Object value, JsonSerializer jser) 
        => jser.Deserialize<TReal>(reader);

    public override void WriteJson(JsonWriter writer, Object value, JsonSerializer jser) 
        => jser.Serialize(writer, value);
}

... और जब deserialize:

        var settings = new JsonSerializerSettings
        {
            Converters = {
                new AbstractConverter<Thing, IThingy>(),
                new AbstractConverter<Thing2, IThingy2>()
            },
        };

        JsonConvert.DeserializeObject(json, type, settings);

1
मैं वास्तव में एक अच्छा संक्षिप्त जवाब पसंद करता हूं जो मेरी समस्या को हल करता है। ऑटोफेक या कुछ भी करने की आवश्यकता नहीं है!
बेन पावर

3
यह कन्वर्टर क्लास की घोषणा के where TReal : TAbstractलिए इसे डालने लायक है: यह सुनिश्चित करने के लिए कि यह टाइप पर जा सकता है
आर्टेमियस

1
एक अधिक पूर्ण जहां हो सकता है where TReal : class, TAbstract, new()
एरिक फिलिप्स

2
मैंने इस कनवर्टर का उपयोग संरचना के साथ भी किया है, मेरा मानना ​​है कि "जहां ट्रायल: टैब्रेट" पर्याप्त है। सबको शुक्रीया।
गिल्डर

2
सोना! जाने के लिए चालाक रास्ता।
स्विसकोड

12

निकोलस वेस्टबी ने एक भयानक लेख में एक महान समाधान प्रदान किया ।

यदि आप JSON को कई संभावित वर्गों में से एक बनाना चाहते हैं जो इस तरह का इंटरफ़ेस लागू करते हैं:

public class Person
{
    public IProfession Profession { get; set; }
}

public interface IProfession
{
    string JobTitle { get; }
}

public class Programming : IProfession
{
    public string JobTitle => "Software Developer";
    public string FavoriteLanguage { get; set; }
}

public class Writing : IProfession
{
    public string JobTitle => "Copywriter";
    public string FavoriteWord { get; set; }
}

public class Samples
{
    public static Person GetProgrammer()
    {
        return new Person()
        {
            Profession = new Programming()
            {
                FavoriteLanguage = "C#"
            }
        };
    }
}

आप एक कस्टम JSON कनवर्टर का उपयोग कर सकते हैं:

public class ProfessionConverter : JsonConverter
{
    public override bool CanWrite => false;
    public override bool CanRead => true;
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(IProfession);
    }
    public override void WriteJson(JsonWriter writer,
        object value, JsonSerializer serializer)
    {
        throw new InvalidOperationException("Use default serialization.");
    }

    public override object ReadJson(JsonReader reader,
        Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        var jsonObject = JObject.Load(reader);
        var profession = default(IProfession);
        switch (jsonObject["JobTitle"].Value())
        {
            case "Software Developer":
                profession = new Programming();
                break;
            case "Copywriter":
                profession = new Writing();
                break;
        }
        serializer.Populate(jsonObject.CreateReader(), profession);
        return profession;
    }
}

और आपको "प्रोफेशन" प्रॉपर्टी को अपने कस्टम कन्वर्टर का उपयोग करने के लिए बताने के लिए JsonConverter विशेषता के साथ सजाने की आवश्यकता होगी:

    public class Person
    {
        [JsonConverter(typeof(ProfessionConverter))]
        public IProfession Profession { get; set; }
    }

और फिर, आप अपनी कक्षा को एक अंतरफलक के साथ रख सकते हैं:

Person person = JsonConvert.DeserializeObject<Person>(jsonString);

8

दो चीजें जो आप आजमा सकते हैं:

एक कोशिश / लागू करें मॉडल:

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

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

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

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

public class Magnate : IPerson {
  public string Name { get; set; }
  public string IndustryName { get; set; }
}

public class Heir: IPerson {
  public string Name { get; set; }
  public IPerson Benefactor { get; set; }
}

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

  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  {
    // pseudo-code
    object richDude = serializer.Deserialize<Heir>(reader);

    if (richDude == null)
    {
        richDude = serializer.Deserialize<Magnate>(reader);
    }

    if (richDude == null)
    {
        richDude = serializer.Deserialize<Tycoon>(reader);
    }

    return richDude;
  }

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

या, यदि आप अपने ऑब्जेक्ट मॉडल में ऐसा कर सकते हैं, तो आईपर्सन और आपके पत्ते की वस्तुओं के बीच एक ठोस आधार वर्ग को लागू करें, और इसके लिए deserialize करें।

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


जिस पैमाने पर मुझे काम करना है, उसके कारण एक कोशिश / पार्स मॉडल संभव नहीं है। मुझे एम्बेडेड JSON ऑब्जेक्ट्स का प्रतिनिधित्व करने के लिए और भी सैकड़ों स्टब / हेल्पर ऑब्जेक्ट्स के साथ सैकड़ों बेस ऑब्जेक्ट्स के दायरे पर विचार करना होगा। यह ऑब्जेक्ट मॉडल को बदलने के लिए सवाल से बाहर नहीं है, लेकिन गुणों में एक ठोस आधार वर्ग का उपयोग नहीं करेगा जो हमें इकाई परीक्षण के लिए आइटमों को नकली करने में असमर्थ बनाता है? या मैं किसी तरह उस पिछड़े हो रहा हूँ?
tmesser

आप अभी भी IPerson से एक नकली लागू कर सकते हैं - ध्यान दें कि Organisation.Owner संपत्ति का प्रकार अभी भी IPerson है। लेकिन एक मनमाना लक्ष्य के deserialization के लिए आपको एक ठोस प्रकार वापस करना होगा। यदि आपके पास प्रकार की परिभाषा नहीं है और आप उन संपत्तियों के न्यूनतम सेट को परिभाषित नहीं कर सकते हैं जिनके लिए आपके कोड की आवश्यकता होगी, तो आपका अंतिम उपाय एक कुंजी / मूल्य बैग जैसा है। अपने फेसबुक उदाहरण टिप्पणी का उपयोग करना - क्या आप एक जवाब में पोस्ट कर सकते हैं कि आपका (एक या कई) इलोकेशन का कार्यान्वयन कैसा दिखता है? इससे चीजों को आगे बढ़ने में मदद मिल सकती है।
19

चूँकि प्राथमिक उम्मीद मज़ाक कर रही है, इसलिए इलोकेशन इंटरफ़ेस वास्तव में, स्थान ठोस वस्तु के लिए केवल एक पहलू है। एक त्वरित उदाहरण जो मैंने अभी काम किया है वह इंटरफ़ेस के लिए कुछ इस तरह ( pastebin.com/mWQtqGnB ) और ठोस वस्तु के लिए यह ( pastebin.com/TdJ6cqWV ) होगा।
tmesser

और अगले चरण पर जाने के लिए, यह एक उदाहरण है कि IPage कैसा दिखेगा ( pastebin.com/iuGifQXp ) और पेज ( pastebin.com/ebqLxzvm )। निश्चित रूप से, समस्या यह है कि पेज के डिसेरिएलाइज़ेशन के दौरान आम तौर पर ठीक काम होगा, यह तब होगा जब यह ILS संपत्ति को मिलेगा।
tmesser

ठीक है, इसलिए उन वस्तुओं के बारे में सोच रहे हैं जो आप वास्तव में स्क्रैप कर रहे हैं और डिसेररलाइज़ कर रहे हैं - क्या यह आम तौर पर ऐसा होता है कि JSON डेटा एकल कंक्रीट क्लास परिभाषा के अनुरूप है? अर्थ (काल्पनिक रूप से) आप अतिरिक्त गुणों के साथ "स्थानों" का सामना नहीं करेंगे, जो कि स्थान को अनुपयुक्त वस्तु के लिए ठोस प्रकार के रूप में उपयोग करने के लिए अनुपयुक्त बना देगा? यदि ऐसा है, तो एक "स्थानसंकेतक" के साथ पृष्ठ की ILocation संपत्ति को जिम्मेदार ठहराते हुए काम करना चाहिए। यदि नहीं, और ऐसा इसलिए है क्योंकि JSON डेटा हमेशा एक कठोर या सुसंगत संरचना (जैसे ILocation) के अनुरूप नहीं है, तब (... जारी रखा)
mcw

8

मुझे यह उपयोगी लगा। आप भी कर सकते हैं।

उदाहरण उपयोग

public class Parent
{
    [JsonConverter(typeof(InterfaceConverter<IChildModel, ChildModel>))]
    IChildModel Child { get; set; }
}

कस्टम निर्माण कनवर्टर

public class InterfaceConverter<TInterface, TConcrete> : CustomCreationConverter<TInterface>
    where TConcrete : TInterface, new()
{
    public override TInterface Create(Type objectType)
    {
        return new TConcrete();
    }
}

Json.NET प्रलेखन


1
नहीं एक व्यावहारिक समाधान। सूचियों को संबोधित नहीं करता है और हर जगह सज्जाकार / एनोटेशन छिड़कने की ओर जाता है।
सीन एंडरसन

5

उन लोगों के लिए, जो कि ओलिवर द्वारा संदर्भित किए गए कंकरीट लाईट टाइपकॉन के बारे में उत्सुक हो सकते हैं, यहां मेरा प्रयास है:

public class ConcreteListTypeConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface 
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var res = serializer.Deserialize<List<TImplementation>>(reader);
        return res.ConvertAll(x => (TInterface) x);
    }

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

1
मैं ओवरराइड से भ्रमित हूं CanConvert(Type objectType) { return true;}। ऐसा लगता है कि यह कितना उपयोगी है? मैं गलत हो सकता हूं, लेकिन एक छोटे अनुभवहीन सेनानी को बताने जैसा नहीं है कि वे लड़ाई जीतने जा रहे हैं चाहे कोई भी प्रतिद्वंद्वी क्यों न हो?
शेफ_कोड

4

इसके लायक होने के लिए, मैंने इसे सबसे अधिक भाग के लिए खुद को संभालने के लिए समाप्त कर दिया। प्रत्येक ऑब्जेक्ट में एक Deserialize (string jsonStream) विधि होती है। इसके कुछ स्निपेट:

JObject parsedJson = this.ParseJson(jsonStream);
object thingyObjectJson = (object)parsedJson["thing"];
this.Thing = new Thingy(Convert.ToString(thingyObjectJson));

इस मामले में, नया थिंगी (स्ट्रिंग) एक कंस्ट्रक्टर है जो उचित कंक्रीट प्रकार के Deserialize (स्ट्रिंग jsonStream) विधि को कॉल करेगा । यह योजना तब तक अधोमुखी और नीचे की ओर जाती रहेगी जब तक कि आप बेस पॉइंट तक नहीं पहुंच जाते हैं जो कि json.NET सिर्फ संभाल सकता है।

this.Name = (string)parsedJson["name"];
this.CreatedTime = DateTime.Parse((string)parsedJson["created_time"]);

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


4

मान लीजिए कि निम्नलिखित की तरह एक ऑटोफेक सेटिंग:

public class AutofacContractResolver : DefaultContractResolver
{
    private readonly IContainer _container;

    public AutofacContractResolver(IContainer container)
    {
        _container = container;
    }

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

        // use Autofac to create types that have been registered with it
        if (_container.IsRegistered(objectType))
        {
           contract.DefaultCreator = () => _container.Resolve(objectType);
        }  

        return contract;
    }
}

फिर, मान लीजिए कि आपकी कक्षा इस प्रकार है:

public class TaskController
{
    private readonly ITaskRepository _repository;
    private readonly ILogger _logger;

    public TaskController(ITaskRepository repository, ILogger logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public ITaskRepository Repository
    {
        get { return _repository; }
    }

    public ILogger Logger
    {
        get { return _logger; }
    }
}

इसलिए, deserialization में रिज़ॉल्वर का उपयोग इस तरह हो सकता है:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<TaskRepository>().As<ITaskRepository>();
builder.RegisterType<TaskController>();
builder.Register(c => new LogService(new DateTime(2000, 12, 12))).As<ILogger>();

IContainer container = builder.Build();

AutofacContractResolver contractResolver = new AutofacContractResolver(container);

string json = @"{
      'Logger': {
        'Level':'Debug'
      }
}";

// ITaskRespository and ILogger constructor parameters are injected by Autofac 
TaskController controller = JsonConvert.DeserializeObject<TaskController>(json, new JsonSerializerSettings
{
    ContractResolver = contractResolver
});

Console.WriteLine(controller.Repository.GetType().Name);

आप http://www.newtonsoft.com/json/help/html/DeserializeWithD dependencyInjection.htm में अधिक विवरण देख सकते हैं


मैं इसे सर्वश्रेष्ठ समाधान के रूप में वोट करूंगा। DI को इन दिनों c # web devs द्वारा व्यापक रूप से उपयोग किया गया है, और यह रिज़ॉल्वर द्वारा उन प्रकार के रूपांतरण को संभालने के लिए एक केंद्रीकृत स्थान के रूप में अच्छी तरह से फिट है।
एप्लेटोव जू

3

कोई भी वस्तु कभी भी एक इंगलिश नहीं होगी क्योंकि सभी परिभाषा के अनुसार इंटरफेस हैं।

आपके पास जो ऑब्जेक्ट पहले सिलिअरी था, वह कुछ ठोस प्रकार का था, जो अमूर्त इंटरफ़ेस को लागू करता था । आपको इसी ठोस वर्ग को क्रमबद्ध डेटा को पुनर्जीवित करना होगा।

परिणामी वस्तु तब कुछ प्रकार की होगी जो आपके लिए देख रहे सार इंटरफ़ेस को लागू करती है ।

से प्रलेखन यह इस प्रकार है कि आप उपयोग कर सकते हैं

(Thingy)JsonConvert.DeserializeObject(jsonString, typeof(Thingy));

जब JSON.NET को कंक्रीट प्रकार के बारे में सूचित करने के लिए deserializing।


यह ठीक एक साल पहले की पोस्ट से है जिसका मैं उल्लेख कर रहा था। एकमात्र मुख्य सुझाव (कस्टम कन्वर्टर्स लिखना) उस पैमाने के साथ बहुत संभव नहीं है जिस पैमाने पर मैं विचार करने के लिए मजबूर हूं। JSON.NET ने हस्तक्षेप वर्ष में बहुत कुछ बदल दिया है। मैं पूरी तरह से एक वर्ग और एक इंटरफेस के बीच के अंतर को समझता हूं, लेकिन C # इंटरफ़ेस से अंतर्निहित रूपांतरण का भी समर्थन करता है जो टाइपिंग के संबंध में इंटरफ़ेस को लागू करता है। मैं अनिवार्य रूप से पूछ रहा हूं कि क्या JSON.NET को बताने का कोई तरीका है जो इस इंटरफ़ेस को लागू करेगा।
tmesser

यह सब वहाँ था उत्तर में मैंने आपको बताया। सुनिश्चित करें कि वहाँ एक _typeसंपत्ति है जो उपयोग करने के लिए ठोस प्रकार को इंगित करता है।
सीन किन्से

और मुझे दृढ़ता से संदेह है कि C # किसी भी प्रकार के संकेत के बिना एक ठोस के रूप में एक इंटरफ़ेस के रूप में घोषित चर से 'निहित' टाइपकास्टिंग का समर्थन करता है।
सीन किनसे

जब तक मैंने इसे गलत नहीं समझा, _type संपत्ति को JSON में धारावाहिक रूप से होना चाहिए था। यह ठीक काम करता है यदि आप केवल वही है जो आप पहले से ही serialized है, लेकिन यह है कि यहाँ क्या हो रहा है नहीं है। मैं JSON को कई साइटों से खींच रहा हूं जो उस मानक का पालन नहीं करने वाली हैं।
tmesser

@ YYY - क्या आप स्रोत JSON से क्रमिक और डिजायरलाइज़ेशन दोनों को नियंत्रित करते हैं? क्योंकि अंत में आपको या तो सीरियलाइज्ड JSON में कंक्रीट प्रकार को एम्बेड करने की आवश्यकता होती है, जब उपयोग करने के लिए संकेत दिया जाता है या आपको किसी प्रकार के प्रयास / पार्स मॉडल का उपयोग करने की आवश्यकता होती है जो रनटाइम के दौरान कंक्रीट प्रकार का पता लगाता है / प्रयास करता है। और उपयुक्त deserializer आह्वान करें।
एमसीडब्ल्यू

3

मेरा यह समाधान, जो मुझे पसंद है, क्योंकि यह सामान्य है, इस प्रकार है:

/// <summary>
/// Automagically convert known interfaces to (specific) concrete classes on deserialisation
/// </summary>
public class WithMocksJsonConverter : JsonConverter
{
    /// <summary>
    /// The interfaces I know how to instantiate mapped to the classes with which I shall instantiate them, as a Dictionary.
    /// </summary>
    private readonly Dictionary<Type,Type> conversions = new Dictionary<Type,Type>() { 
        { typeof(IOne), typeof(MockOne) },
        { typeof(ITwo), typeof(MockTwo) },
        { typeof(IThree), typeof(MockThree) },
        { typeof(IFour), typeof(MockFour) }
    };

    /// <summary>
    /// Can I convert an object of this type?
    /// </summary>
    /// <param name="objectType">The type under consideration</param>
    /// <returns>True if I can convert the type under consideration, else false.</returns>
    public override bool CanConvert(Type objectType)
    {
        return conversions.Keys.Contains(objectType);
    }

    /// <summary>
    /// Attempt to read an object of the specified type from this reader.
    /// </summary>
    /// <param name="reader">The reader from which I read.</param>
    /// <param name="objectType">The type of object I'm trying to read, anticipated to be one I can convert.</param>
    /// <param name="existingValue">The existing value of the object being read.</param>
    /// <param name="serializer">The serializer invoking this request.</param>
    /// <returns>An object of the type into which I convert the specified objectType.</returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return serializer.Deserialize(reader, this.conversions[objectType]);
        }
        catch (Exception)
        {
            throw new NotSupportedException(string.Format("Type {0} unexpected.", objectType));
        }
    }

    /// <summary>
    /// Not yet implemented.
    /// </summary>
    /// <param name="writer">The writer to which I would write.</param>
    /// <param name="value">The value I am attempting to write.</param>
    /// <param name="serializer">the serializer invoking this request.</param>
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

}

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


3

कई वर्षों से और मेरे पास एक समान मुद्दा था। मेरे मामले में भारी नेस्टेड इंटरफेस थे और रनटाइम के दौरान ठोस कक्षाएं बनाने के लिए एक प्राथमिकता थी ताकि यह सामान्य वर्ग के साथ काम करे।

मैंने रन टाइम पर एक प्रॉक्सी क्लास बनाने का फैसला किया जो न्यूटनसॉफ्ट द्वारा लौटाए गए ऑब्जेक्ट को लपेटता है।

इस दृष्टिकोण का लाभ यह है कि इसे वर्ग के ठोस कार्यान्वयन की आवश्यकता नहीं है और स्वचालित रूप से नेस्टेड इंटरफेस की किसी भी गहराई को संभाल सकता है। आप इसके बारे में मेरे ब्लॉग पर अधिक देख सकते हैं ।

using Castle.DynamicProxy;
using Newtonsoft.Json.Linq;
using System;
using System.Reflection;

namespace LL.Utilities.Std.Json
{
    public static class JObjectExtension
    {
        private static ProxyGenerator _generator = new ProxyGenerator();

        public static dynamic toProxy(this JObject targetObject, Type interfaceType) 
        {
            return _generator.CreateInterfaceProxyWithoutTarget(interfaceType, new JObjectInterceptor(targetObject));
        }

        public static InterfaceType toProxy<InterfaceType>(this JObject targetObject)
        {

            return toProxy(targetObject, typeof(InterfaceType));
        }
    }

    [Serializable]
    public class JObjectInterceptor : IInterceptor
    {
        private JObject _target;

        public JObjectInterceptor(JObject target)
        {
            _target = target;
        }
        public void Intercept(IInvocation invocation)
        {

            var methodName = invocation.Method.Name;
            if(invocation.Method.IsSpecialName && methodName.StartsWith("get_"))
            {
                var returnType = invocation.Method.ReturnType;
                methodName = methodName.Substring(4);

                if (_target == null || _target[methodName] == null)
                {
                    if (returnType.GetTypeInfo().IsPrimitive || returnType.Equals(typeof(string)))
                    {

                        invocation.ReturnValue = null;
                        return;
                    }

                }

                if (returnType.GetTypeInfo().IsPrimitive || returnType.Equals(typeof(string)))
                {
                    invocation.ReturnValue = _target[methodName].ToObject(returnType);
                }
                else
                {
                    invocation.ReturnValue = ((JObject)_target[methodName]).toProxy(returnType);
                }
            }
            else
            {
                throw new NotImplementedException("Only get accessors are implemented in proxy");
            }

        }
    }



}

उपयोग:

var jObj = JObject.Parse(input);
InterfaceType proxyObject = jObj.toProxy<InterfaceType>();

धन्यवाद! यह एकमात्र उत्तर है आने वाले जन्स पर प्रतिबंधों को लागू किए बिना डायनेमिक टाइपिंग (डक टाइपिंग) का सही समर्थन करता है।
फिलिप पिपेट

कोई दिक्कत नहीं है। मुझे यह देखकर थोड़ा आश्चर्य हुआ कि वहाँ कुछ भी नहीं था। यह उस मूल उदाहरण से थोड़ा आगे बढ़ गया है इसलिए मैंने कोड साझा करने का निर्णय लिया। github.com/sudsy/JsonDuckTyper । मैंने इसे Nson पर JsonDuckTyper के रूप में भी प्रकाशित किया। यदि आप पाते हैं कि आप इसे बढ़ाना चाहते हैं, तो बस मुझे एक पीआर भेजें और मुझे उपकृत करने में खुशी होगी।
सूदसी

जब मैं इस क्षेत्र में एक समाधान की तलाश में था तो मुझे github.com/ekonbenefits/impromptu-interface भी आया । यह मेरे मामले में काम नहीं करता है क्योंकि यह डॉटनेट कोर 1.0 का समर्थन नहीं करता है लेकिन यह आपके लिए काम कर सकता है।
सूदसी

मैंने Impromptu Interface के साथ प्रयास किया था, लेकिन Json.Net PopulateObjectImpromptu Interface द्वारा उत्पन्न प्रॉक्सी पर खुश नहीं था । मैंने दुर्भाग्य से डक टाइपिंग के लिए जाना छोड़ दिया - एक कस्टम जोंस कॉन्ट्रैक्ट सीरियल बनाने के लिए बस आसान था जो अनुरोधित इंटरफ़ेस के मौजूदा कार्यान्वयन को खोजने के लिए प्रतिबिंब का उपयोग करता था और उसी का उपयोग कर रहा था।
22

1

इस JsonKnownTypes का उपयोग करें, यह उपयोग करने के लिए बहुत समान है, यह सिर्फ json में भेदभाव करनेवाला जोड़ता है :

[JsonConverter(typeof(JsonKnownTypeConverter<Interface1>))]
[JsonKnownType(typeof(MyClass), "myClass")]
public interface Interface1
{  }
public class MyClass : Interface1
{
    public string Something;
}

अब जब आप json में ऑब्जेक्ट को क्रमबद्ध करेंगे, तो मूल्य के "$type"साथ जोड़ "myClass"दिया जाएगा और इसका उपयोग deserialize के लिए किया जाएगा

Json:

{"Something":"something", "$type":"derived"}

0

मेरा समाधान कंस्ट्रक्टर में इंटरफ़ेस तत्वों को जोड़ा गया था।

public class Customer: ICustomer{
     public Customer(Details details){
          Details = details;
     }

     [JsonProperty("Details",NullValueHnadling = NullValueHandling.Ignore)]
     public IDetails Details {get; set;}
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.