JSON.NET- এ deserialization জন্য ইন্টারফেস কাস্টিং


128

আমি এমন একটি পাঠক সেট আপ করার চেষ্টা করছি যা বিভিন্ন ওয়েবসাইট থেকে জেএসএন অবজেক্টগুলিতে নেবে (তথ্য স্ক্র্যাপিং মনে করে) এবং সেগুলিকে সি # অবজেক্টে অনুবাদ করবে। আমি বর্তমানে ডেসারিয়ালাইজেশন প্রক্রিয়াটির জন্য JSON.NET ব্যবহার করছি। আমি যে সমস্যাটি চালাচ্ছি তা হ'ল এটি কোনও শ্রেণিতে ইন্টারফেস-স্তরের বৈশিষ্ট্যগুলি কীভাবে পরিচালনা করতে হয় তা জানে না। তাই প্রকৃতির কিছু:

public IThingy Thing

ত্রুটি উত্পাদন করবে:

আইটিহিং টাইপের উদাহরণ তৈরি করতে পারেনি। প্রকারটি একটি ইন্টারফেস বা বিমূর্ত শ্রেণি এবং তা ইনস্ট্যান্ট করা যায় না cannot

থিংয়ের বিপরীতে এটি আইটিঙ্গি হওয়া অপেক্ষাকৃত গুরুত্বপূর্ণ, যেহেতু আমি যে কোডটিতে কাজ করছি সেটিকে সংবেদনশীল হিসাবে বিবেচনা করা হয় এবং ইউনিট টেস্টিং অত্যন্ত গুরুত্বপূর্ণ। পারমাণবিক পরীক্ষার স্ক্রিপ্টগুলির জন্য বস্তুর উপহাস করা থিংয়ের মতো সম্পূর্ণ সজ্জিত বস্তুর দ্বারা সম্ভব নয়। তারা অবশ্যই একটি ইন্টারফেস হতে হবে।

আমি এখন থেকে JSON.NET এর ডকুমেন্টেশনগুলিতে কিছুক্ষণের জন্য ছুঁয়েছি এবং এই সাইটের সাথে সম্পর্কিত যে প্রশ্নগুলি আমি খুঁজে পেতে পারি সেগুলি সব এক বছর আগে থেকেই from কোন সাহায্য?

এছাড়াও, যদি এটি গুরুত্বপূর্ণ হয় তবে আমার অ্যাপ্লিকেশনটি নেট নেট 4.0.০ এ লিখিত আছে।


উত্তর:


115

@ সামুয়ালড্যাভিস একটি সম্পর্কিত প্রশ্নে একটি দুর্দান্ত সমাধান দিয়েছেন , যা আমি এখানে সংক্ষেপে জানাব ।

আপনার যদি ইন্টারন্যাশনাল বৈশিষ্ট্যযুক্ত কোনও JSON স্ট্রিমকে একটি কংক্রিট ক্লাসে ডিজিজালাইজ করতে হয় তবে আপনি কংক্রিটের ক্লাসগুলিকে বর্গের কোনও কনস্ট্রাক্টরের পরামিতি হিসাবে অন্তর্ভুক্ত করতে পারেন ! নিউটনসফ্ট ডিজিজারাইজার যথেষ্ট স্মার্ট যাতে এটি নির্ধারণের জন্য concrete কংক্রিটের ক্লাসগুলি ব্যবহার করা উচিত।

এখানে একটি উদাহরণ:

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
আইকোলিকেশন দিয়ে এটি কীভাবে কাজ করবে? আইকোলিকেশন <জিগুয়েস্ট> অতিথিরা {পাবেন; সেট করুন}
ডাঃস্যামিডি

12
এটি আইকোলিকেশন <কনক্রিটক্লাস> নিয়ে কাজ করে, তাই আইকোলিকেশন <গুয়েস্ট> কাজ করে। ঠিক একটি এফওয়াইআই হিসাবে, আপনি আপনার নির্মাণকারীর উপর [জসনকন্সট্রাক্টর] বৈশিষ্ট্যটি রাখতে পারেন যাতে এটি আপনার ডিফল্টরূপে ব্যবহার করতে পারে যদি আপনার একাধিক কনস্ট্রাক্টর থাকে
DrSammyD

6
আমি একই সমস্যার সাথে আটকে আছি, আমার ক্ষেত্রে আমার ইন্টারফেসের বেশ কয়েকটি বাস্তবায়ন হয়েছে (উদাহরণস্বরূপ ইন্টারফেসটি ILocation) তাই মাইলোকেশন, ভিআইপিলোকেশন, অর্ডারিনলোকেশনের মতো ক্লাস থাকলে কী হবে। এগুলিকে কীভাবে অবস্থানের সম্পত্তিটিতে ম্যাপ করবেন? আপনার যদি মাইলোকেশনের মতো একটি বাস্তবায়ন সহজ হয় তবে আইলোকেশনের একাধিক বাস্তবায়ন হলে এটি কীভাবে করবেন?
অন্য

10
আপনার যদি একাধিক কনস্ট্রাক্টর থাকে তবে আপনি আপনার বিশেষ কনস্ট্রাক্টরটিকে [JsonConstructor]বৈশিষ্ট্যের সাথে চিহ্নিত করতে পারেন ।
ডাঃ রব ল্যাং

26
এটি মোটেও ঠিক নয়। ইন্টারফেস ব্যবহারের বিষয়টি হ'ল নির্ভরতা ইনজেকশন ব্যবহার করা, তবে আপনার কনস্ট্রাক্টরের দ্বারা প্রয়োজনীয় কোনও অবজেক্ট টাইপ করা প্যারামিটার দিয়ে এটি করা আপনি সম্পত্তি হিসাবে একটি ইন্টারফেস থাকার বিষয়টি পুরোপুরি স্ক্রু করে ফেলেন।
জের্মে মেভেল

57

( এই প্রশ্ন থেকে অনুলিপি করা )

যেসব ক্ষেত্রে আগত জেএসওএন-এর উপর আমার নিয়ন্ত্রণ ছিল না (এবং এটি নিশ্চিত করতে পারে না যে এটিতে $ ধরণের সম্পত্তি রয়েছে) আমি একটি কাস্টম রূপান্তরকারী লিখেছি যা কেবল আপনাকে কংক্রিটের প্রকারটি স্পষ্টভাবে নির্দিষ্ট করার অনুমতি দেয়:

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>প্রয়োগ পোস্ট করতে পারবেন ?
মাইকেল 21

2
এবং আপনার যদি আইসোমিংথিংয়ের দুটি প্রয়োগকারী থাকে?
বিডানিয়েল

56

কেন একটি রূপান্তরকারী ব্যবহার? Newtonsoft.Jsonএই সঠিক সমস্যাটি সমাধান করার জন্য একটি দেশীয় কার্যকারিতা রয়েছে :

সেট TypeNameHandlingমধ্যে JsonSerializerSettingsথেকেTypeNameHandling.Auto

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

এটি প্রতিটি ধরণের জসনকে রাখবে, এটি কোনও প্রকারের কংক্রিট উদাহরণ হিসাবে নয় তবে একটি ইন্টারফেস বা বিমূর্ত শ্রেণীরূপে রাখা হয়।

নিশ্চিত করুন যে আপনি সিরিয়ালাইজেশন এবং ডিসরিয়ালাইজেশনের জন্য একই সেটিংস ব্যবহার করছেন

আমি এটি পরীক্ষা করেছি এবং এটি তালিকাগুলির মতো মনোযোগের মতো কাজ করে।

সাইটের লিঙ্কগুলির সাথে অনুসন্ধান ফলাফল ওয়েব ফলাফল

⚠️ সতর্কতা :

এটি কেবল পরিচিত এবং বিশ্বস্ত উত্স থেকে জসনের জন্য ব্যবহার করুন। ব্যবহারকারী স্নিপস্নিপসনিপ সঠিকভাবে উল্লেখ করেছেন যে এটি প্রকৃতপক্ষে একটি কর্মক্ষমতা।

দেখুন CA2328 এবং SCS0028 আরও তথ্যের জন্য।


উত্স এবং একটি বিকল্প ম্যানুয়াল বাস্তবায়ন: কোড ব্লগের ভিতরে


3
পারফেক্ট, এই আমাকে একটা দ্রুত ও মলিন গভীর ক্লোন (জন্য সাহায্য করেছে stackoverflow.com/questions/78536/deep-cloning-objects )
Compufreak

1
@ শিমি অবজেক্টস: "JSON অবজেক্ট স্ট্রাকচারটিতে সিরিয়ালাইজ করার সময় .NET টাইপের নাম অন্তর্ভুক্ত করুন।" স্বতঃ: সিরিয়ালযুক্ত বস্তুর ধরণ যখন ঘোষিত প্রকারের মতো না হয় তখন .NET টাইপের নাম অন্তর্ভুক্ত করুন। নোট করুন যে এটিতে ডিফল্টরূপে মূল সিরিয়ালাইজড অবজেক্টটি অন্তর্ভুক্ত নয়। জেএসএনে মূল অবজেক্টের ধরণের নাম অন্তর্ভুক্ত করতে আপনাকে অবশ্যই সিরিয়ালাইজঅবজেক্ট (অবজেক্ট, প্রকার, জসনসিরাইজারসেটেটিংস) বা সিরিয়ালাইজ (জসনরাইটার, অবজেক্ট, টাইপ) সহ একটি মূল ধরণের অবজেক্ট নির্দিষ্ট করতে হবে। "সূত্র: newtonsoft.com/json/help/html/…
মাফেই

4
আমি এটি কেবল ডিসিরিয়ালাইজেশনে চেষ্টা করেছি এবং এটি কার্যকর হয় না। এই স্ট্যাক ওভারফ্লো প্রশ্নটির বিষয় লাইনটি হ'ল, "জেএসওএন.নেট-এ deserialization জন্য ইন্টারফেস কাস্টিং"
জাস্টিন রুসো

3
@ জাস্টিন রুসো কেবল তখনই কাজ করবে যখন
জসন

3
দ্রুত, নোংরা না হলে সমাধান করুন। আপনি যদি কনফিগারেশনগুলি কেবল সিরিয়ালাইজ করে থাকেন তবে এটি কাজ করে। রূপান্তরকারী তৈরি করতে বিকাশকে থামিয়ে দেয় এবং প্রতিটি ইনজেকশনের সম্পত্তি সজ্জিত করে অবশ্যই মারধর করে। সিরিয়ালাইজার.TypeNameHandling = TypeNameHandling.Auto; জসনকনভার্ট.ডাফল্টসেটেটিংস ()। টাইপনামহ্যান্ডলিং = টাইপনামহ্যান্ডলিং.আউটো;
শান অ্যান্ডারসন

39

ইন্টারফেসের একাধিক প্রয়োগের ডিসিরিয়ালাইজেশন সক্ষম করতে আপনি JsonConverter ব্যবহার করতে পারেন, তবে কোনও বৈশিষ্ট্যের মাধ্যমে নয়:

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

ডিটিওজেসন কনভার্টর প্রতিটি ইন্টারফেসকে একটি কংক্রিট বাস্তবায়নের সাথে মানচিত্র দেয়:

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);
    }
}

ডিটিওজেসন কনভার্টার কেবল ডিসরিয়ালাইজারের জন্য প্রয়োজনীয়। সিরিয়ালাইজেশন প্রক্রিয়াটি অপরিবর্তিত। জসন অবজেক্টের কংক্রিটের প্রকারের নাম এম্বেড করার দরকার নেই।

এই এসও পোস্টটি জেনেরিক জসনকনভার্টারের সাথে আরও এক ধাপ এগিয়ে একই সমাধান সরবরাহ করে।


সিরিয়ালাইজারে রাইটিংজেসন পদ্ধতির কলটি হবে না erial
ট্রায়ঙ্কো

এটি করা উচিত নয়, যদি ক্যানকনভার্ট () পদ্ধতিটি একটি ধারাবাহিক ফলাফল দেয়।
এরিক বোমেন্ডিল

3
আপনি FullNameযখন সরাসরি প্রকারের সাথে কেবল তুলনা করতে পারেন তখন কেন আপনি তুলনা করছেন ?
অ্যালেক্স ঝুকভস্কিয়

ধরণের তুলনা করাও ঠিক is
এরিক বোমেন্ডিল

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
আমি এই রূপান্তরটি স্ট্রাক্টের সাথেও ব্যবহার করেছি, আমি বিশ্বাস করি "যেখানে ট্রায়াল: টিএবস্ট্রাক্ট" যথেষ্ট is সবাইকে ধন্যবাদ.
গিল্ডার

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;
    }
}

এবং আপনার কাস্টম রূপান্তরকারীটি এটি ব্যবহার করতে জানাতে আপনার একটি জসনকনভার্টর বৈশিষ্ট্য সহ "পেশা" বৈশিষ্ট্যটি সাজাতে হবে:

    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();
  }
}

অথবা, আপনি যদি নিজের অবজেক্টের মডেলটিতে এটি করতে পারেন তবে আইফারসন এবং আপনার পাতার বস্তুর মধ্যে একটি কংক্রিট বেস ক্লাস বাস্তবায়ন করুন এবং এটির ডিজিটালাইজ করুন।

প্রথমটি সম্ভাব্যভাবে রানটাইমে ব্যর্থ হতে পারে, দ্বিতীয়টির জন্য আপনার অবজেক্টের মডেলটির পরিবর্তন প্রয়োজন এবং আউটপুটকে সর্বনিম্ন সাধারণ ডিনোমিনেটরে একত্র করে।


আমার যে স্কেলটি নিয়ে কাজ করতে হবে তার কারণে চেষ্টা / পার্স মডেলটি সম্ভবপর নয়। এম্বেড হওয়া JSON অবজেক্টগুলিতে প্রচুর ঘটনাকে উপস্থাপন করতে আমাকে আরও কয়েক শতাধিক স্টাব / হেল্পার অবজেক্ট সহ শত শত বেস অবজেক্টের সুযোগ বিবেচনা করতে হবে। এটি অবজেক্টের মডেলটি পরিবর্তন করার প্রশ্নের বাইরে নয়, তবে বৈশিষ্ট্যগুলিতে একটি কংক্রিট বেস ক্লাস ব্যবহার করা আমাদের একক পরীক্ষার জন্য আইটেমগুলি উপহাস করতে অক্ষম করে না? নাকি আমি কোনওভাবেই পিছিয়ে আছি?
tmesser

আপনি এখনও আইফারসন থেকে একটি মক প্রয়োগ করতে পারেন - নোট করুন যে সংগঠনের ধরণ Oউনার সম্পত্তিটি এখনও আইপারসন। কিন্তু একটি স্বেচ্ছাসেবী লক্ষ্যটি deserialization জন্য আপনাকে একটি কংক্রিট টাইপ ফিরে আসতে হবে। যদি আপনি টাইপ সংজ্ঞাটির মালিক না হন এবং আপনি আপনার কোডের জন্য প্রয়োজনীয় সংখ্যার সর্বনিম্ন সেটটি সংজ্ঞায়িত করতে না পারেন তবে আপনার শেষ অবলম্বনটি কী / মান ব্যাগের মতো কিছু। আপনার ফেসবুক উদাহরণ মন্তব্য ব্যবহার করে - আপনি কি আইওলোকেশনের (এক বা একাধিক) বাস্তবায়ন দেখতে দেখতে একটি উত্তরে পোস্ট করতে পারেন? এটি জিনিসগুলিকে এগিয়ে নিয়ে যেতে সহায়তা করতে পারে।
এমসিডব্লিউ

যেহেতু প্রাথমিক আশা উপহাস করছে, আইলোকেশন ইন্টারফেসটি সত্যই, অবস্থান কংক্রিট অবজেক্টের জন্য কেবল একটি মুখোমুখি। একটি দ্রুত উদাহরণস্বরূপ আমি শুধু কাজ এই (ভালো কিছু হবে pastebin.com/mWQtqGnB ইন্টারফেস এবং এই (জন্য) pastebin.com/TdJ6cqWV কংক্রিট বস্তুর জন্য)।
tmesser

এবং পরবর্তী পদক্ষেপে যেতে, এটি আইপ্যাজ দেখতে কেমন হবে তার একটি উদাহরণ ( পেস্টবিন. com/iuGifQXp ) এবং পৃষ্ঠা ( পেস্টবিন . com/ebqLxzvm )। অবশ্যই সমস্যাটি হ'ল যখন পৃষ্ঠাটির deserialization সাধারণত ভাল কাজ করবে, যখন এটি ILocation সম্পত্তির কাছে যায় তখন এটি শ্বাসরোধ করবে।
tmesser

ঠিক আছে, সুতরাং আপনি যে জিনিসগুলি আসলে স্ক্র্যাপিং এবং ডিসরিওলাইজ করছেন সেগুলি সম্পর্কে চিন্তাভাবনা - এটি কি সাধারণভাবে হয় যে JSON ডেটা একটি একক কংক্রিট শ্রেণির সংজ্ঞার সাথে সামঞ্জস্যপূর্ণ? অর্থ (অনুমানকৃতভাবে) আপনি অতিরিক্ত বৈশিষ্ট্যগুলির সাথে "অবস্থানগুলি" মুখোমুখি হবেন না যা ডিসরিয়ালাইজড অবজেক্টের জন্য কংক্রিটের ধরণ হিসাবে অবস্থানকে ব্যবহারের অনুপযুক্ত করে তুলবে? যদি তা হয় তবে পৃষ্ঠার আইওলোকেশন সম্পত্তিটিকে "লোকেশনকভার্টার" দিয়ে চিহ্নিত করার কাজ করা উচিত। যদি তা না হয় এবং এটি কারণ যে JSON ডেটা সবসময় একটি অনমনীয় বা সামঞ্জস্যপূর্ণ কাঠামোর (ILocation এর মতো) মেনে চলে না, তারপরে (... চালিয়ে যায়)
এমসিডব্লিউ 20

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

এটির মূল্যের জন্য, আমি নিজেকে বেশিরভাগ অংশে এটি পরিচালনা করতে পেরেছি। প্রতিটি বস্তুর একটি ডিসরিয়ালাইজ (স্ট্রিং জসনস্ট্রিম) পদ্ধতি রয়েছে। এর কয়েকটি স্নিপেট:

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

এই ক্ষেত্রে, নতুন থিঙ্গি (স্ট্রিং) এমন একটি নির্মাতা যা উপযুক্ত কংক্রিটের ধরণের Deserialize (স্ট্রিং জেসসনস্ট্রিম) পদ্ধতিটিকে কল করবে । আপনি যে বেস বেস পয়েন্টগুলি জেসন.এনইটি স্রেফ পরিচালনা করতে পারবেন না সে পর্যন্ত এই স্কিমটি নীচে এবং নীচে যেতে থাকবে।

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/DesrializeWithD dependencyInication.htm এ আরও বিশদ দেখতে পারেন


আমি সেরা সমাধান হিসাবে এটি আপ-ভোট দেব। ডি # আজকাল সি # ওয়েব ডেভস দ্বারা এত বেশি বিস্তৃতভাবে ব্যবহার করা হয়েছে এবং রেজলভারের মাধ্যমে এই ধরণের রূপান্তর পরিচালনা করতে কেন্দ্রীভূত জায়গা হিসাবে এটি দুর্দান্তভাবে ফিট fit
অ্যাপলেটও

3

কোন বস্তু কখনও হবে না একটি IThingy যেমন ইন্টারফেসগুলি সব সংজ্ঞা দ্বারা বিমূর্ত হয়।

আপনার প্রথমে সিরিয়ালযুক্ত করা বস্তুটি ছিল কিছু কংক্রিটের , বিমূর্ত ইন্টারফেসটি প্রয়োগ করে । আপনার ক্রিয়াকলাপযুক্ত ডেটাটিকে পুনরুদ্ধার করতে একই কংক্রিটের শ্রেণি থাকা দরকার ।

ফলস্বরূপ অবজেক্টটি তখন এমন কোনও ধরণের হবে যা আপনি সন্ধান করছেন এমন বিমূর্ত ইন্টারফেসটি প্রয়োগ করে ।

ডকুমেন্টেশন থেকে এটি অনুসরণ করতে পারে যে আপনি ব্যবহার করতে পারেন

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

যখন কংক্রিটের ধরণ সম্পর্কে JSON.NET অবহিত করার জন্য deserializing।


একবছর আগে যে পোস্টটি আমি উল্লেখ করেছি ঠিক সেটাই পোস্ট। একমাত্র প্রধান পরামর্শ (কাস্টম রূপান্তরকারীদের লেখার জন্য) আমি যে স্কেলটি বিবেচনা করতে বাধ্য হই তা মারাত্মকভাবে সম্ভব নয়। মধ্যবর্তী বছরে জেএসএন.এন.টি. আমি একটি শ্রেণি এবং একটি ইন্টারফেসের মধ্যে পার্থক্যটি পুরোপুরি বুঝতে পারি, তবে সি # এছাড়াও একটি ইন্টারফেস থেকে কোনও অবজেক্টে অন্তর্নিহিত রূপান্তরকে সমর্থন করে যা টাইপিংয়ের সাথে ইন্টারফেস প্রয়োগ করে। আমি মূলত জিজ্ঞাসা করছি যে JSON.NET কে বলার কোনও উপায় আছে কি না কোন বস্তু এই ইন্টারফেসটি কার্যকর করবে।
tmesser

আমি আপনাকে যে উত্তরটি দেখিয়েছিলাম সেখানে এটিই ছিল। নিশ্চিত করুন যে এমন কোনও _typeসম্পত্তি রয়েছে যা ব্যবহারের জন্য কংক্রিটের ধরণের সংকেত দেয়।
শান কিনসে

এবং আমি দৃ strongly়ভাবে সন্দেহ করি যে সি # কোনও ধরণের ইঙ্গিত ছাড়াই একটি কংক্রিট টাইপের ইন্টারফেস হিসাবে ঘোষিত ভেরিয়েবল থেকে যে কোনও ধরণের 'অন্তর্নিহিত' টাইপকাস্টিং সমর্থন করে।
শান কিনসে

আমি এটি ভুল না পড়লে, ধরণের সম্পত্তিটি সিরিয়ালায়িত করার জন্য জেএসএনে থাকার কথা ছিল। এটি ইতিমধ্যে কার্যকর হয়েছে যদি আপনি কেবল যা ইতিমধ্যে সিরিয়ালযুক্ত করেছেন তা কেবল ডিজিটালাইজিং করছেন, তবে এখানে কী চলছে তা তা নয়। আমি বেশ কয়েকটি সাইট থেকে জেএসএন টানছি যা সেই মানটিকে অনুসরণ করছে না।
tmesser

@YYY - আপনি উত্স JSON থেকে সিরিয়ালকরণ এবং deserialization উভয়ই নিয়ন্ত্রণ করেন? কারণ শেষ অবধি আপনাকে ডিজিটালাইজ করার সময় কন্ট্রিট টাইপটি সিরিয়ালযুক্ত জেএসওএন-এ এম্বেড করার প্রয়োজন হিসাবে ব্যবহার করতে হবে বা রানটাইমের সময় কংক্রিটের ধরণ সনাক্ত / চেষ্টা করার চেষ্টা করতে পারে এমন কিছু চেষ্টা / পার্স মডেল ব্যবহার করতে হবে এবং উপযুক্ত 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();
    }
}

}

আপনি স্পষ্টত এবং তুচ্ছভাবে একটি কনস্ট্রাক্টর যুক্ত করে এটি আরও সাধারণ রূপান্তরকারী হিসাবে রূপান্তর করতে পারেন যা অভিধান <টাইপ, টাইপ করুন> যার সাহায্যে রূপান্তরগুলির উদাহরণটি পরিবর্তনশীল ইনস্ট্যান্ট করতে পারে type


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>();

ধন্যবাদ! এটিই একমাত্র উত্তর যা সঠিকভাবে গতিশীল টাইপিং (হাঁসের টাইপিং) সমর্থন করে ইনকামিং জসনকে সীমাবদ্ধ না করে restrictions
ফিলিপ পিটল

সমস্যা নেই. বাইরে কিছু নেই দেখে আমি কিছুটা অবাক হয়েছিলাম। এটি সেই আসল উদাহরণ থেকে কিছুটা এগিয়ে গেছে তাই আমি কোডটি ভাগ করে নেওয়ার সিদ্ধান্ত নিয়েছি। github.com/sudsy/JsonDuckTyper । আমি এটিকে জসনডাকটাইপ হিসাবে নুজেটে প্রকাশ করেছি। আপনি যদি এটির উন্নতি করতে চান তবে কেবল আমাকে একটি জনসংযোগ দিন এবং আমি বাধ্য হয়ে খুশি হব।
সুডসি

আমি যখন এই অঞ্চলে সমাধানের সন্ধান করছিলাম তখন আমি github.com/ekonbenefits/impromptu-interface এও এসেছি । এটি আমার ক্ষেত্রে কাজ করে না কারণ এটি ডটনেট কোর 1.0 সমর্থন করে না তবে এটি আপনার পক্ষে কার্যকর হতে পারে।
সুডসি

আমি ইমপ্রম্প্টু ইন্টারফেস দিয়ে চেষ্টা করেছিলাম, তবে জসন.নেট একটি করে খুশি হয়নি PopulateObject ইন্টারফেস দ্বারা উত্পাদিত প্রক্সিটি । দুর্ভাগ্যক্রমে আমি হাঁসের টাইপিংয়ের জন্য ছেড়ে দিয়েছি - অনুরোধযুক্ত ইন্টারফেসের বিদ্যমান বাস্তবায়ন এবং এটি ব্যবহার করে প্রতিচ্ছবি ব্যবহার করে এমন একটি কাস্টম জসন কন্ট্রাক্ট সিরিয়ালাইজার তৈরি করা সহজ ছিল।
ফিলিপ পিটল

1

এই জসনকোনটিপ টাইপগুলি ব্যবহার করুন, এটি ব্যবহারের মতো এটি একইরকম উপায়, এটি কেবল জসনকে বৈষম্যমূলক করে:

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

এখন আপনি যখন জসন-এ বস্তুটি সিরিয়ালাইজ করবেন তখন মান "$type"সহ যুক্ত "myClass"হবে এবং এটি ডিসেরায়ালাইজের জন্য ব্যবহৃত হবে

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;}
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.