c# - বেস ক্লাস অবজেক্টগুলির একটি তালিকা deserialize JSON.NET এ কাস্টম JsonConverter কিভাবে প্রয়োগ করবেন?




deserialization (6)

আমি এখানে দেওয়া JSON.net উদাহরণটি প্রসারিত করার চেষ্টা করছি http://james.newtonking.com/projects/json/help/CustomCreationConverter.html

আমি বেস ক্লাস / ইন্টারফেস থেকে deriving অন্য সাব ক্লাস আছে

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Employee : Person
{
    public string Department { get; set; }
    public string JobTitle { get; set; }
}

public class Artist : Person
{
    public string Skill { get; set; }
}

List<Person> people  = new List<Person>
{
    new Employee(),
    new Employee(),
    new Artist(),
};

আমি জেসনকে <তালিকা> তালিকাতে ফিরে আসার পরে কিভাবে হতাশ করব?

[
  {
    "Department": "Department1",
    "JobTitle": "JobTitle1",
    "FirstName": "FirstName1",
    "LastName": "LastName1"
  },
  {
    "Department": "Department2",
    "JobTitle": "JobTitle2",
    "FirstName": "FirstName2",
    "LastName": "LastName2"
  },
  {
    "Skill": "Painter",
    "FirstName": "FirstName3",
    "LastName": "LastName3"
  }
]

আমি TypeNameHandling JsonSerializer সেটিংস ব্যবহার করতে চাই না। আমি বিশেষভাবে এই হ্যান্ডেল কাস্টম JsonConverter বাস্তবায়ন খুঁজছেন। এই সম্পর্কে ডকুমেন্টেশন এবং উদাহরণ নেট বেশ চমত্কার। আমি JsonConverter ডানদিকে overridden ReadJson () পদ্ধতি বাস্তবায়ন পেতে বলে মনে হচ্ছে না।


ইন্টারফেস হিসাবে একই নামস্পেসে অনেকগুলি বাস্তবায়ন বিদ্যমান থাকবে। সুতরাং, আমি এই সঙ্গে এসেছিলেন:

    public class InterfaceConverter : JsonConverter
    {
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.ReadFrom(reader);
        var typeVariable = this.GetTypeVariable(token);
        if (TypeExtensions.TryParse(typeVariable, out var implimentation))
        { }
        else if (!typeof(IEnumerable).IsAssignableFrom(objectType))
        {
            implimentation = this.GetImplimentedType(objectType);
        }
        else
        {
            var genericArgumentTypes = objectType.GetGenericArguments();
            var innerType = genericArgumentTypes.FirstOrDefault();
            if (innerType == null)
            {
                implimentation = typeof(IEnumerable);
            }
            else
            {
                Type genericType = null;
                if (token.HasAny())
                {
                    var firstItem = token[0];
                    var genericTypeVariable = this.GetTypeVariable(firstItem);
                    TypeExtensions.TryParse(genericTypeVariable, out genericType);
                }

                genericType = genericType ?? this.GetImplimentedType(innerType);
                implimentation = typeof(IEnumerable<>);
                implimentation = implimentation.MakeGenericType(genericType);
            }
        }

        return JsonConvert.DeserializeObject(token.ToString(), implimentation);
    }

    public override bool CanConvert(Type objectType)
    {
        return !typeof(IEnumerable).IsAssignableFrom(objectType) && objectType.IsInterface || typeof(IEnumerable).IsAssignableFrom(objectType) && objectType.GetGenericArguments().Any(t => t.IsInterface);
    }

    protected Type GetImplimentedType(Type interfaceType)
    {
        if (!interfaceType.IsInterface)
        {
            return interfaceType;
        }

        var implimentationQualifiedName = interfaceType.AssemblyQualifiedName?.Replace(interfaceType.Name, interfaceType.Name.Substring(1));
        return implimentationQualifiedName == null ? interfaceType : Type.GetType(implimentationQualifiedName) ?? interfaceType;
    }

    protected string GetTypeVariable(JToken token)
    {
        if (!token.HasAny())
        {
            return null;
        }

        return token.Type != JTokenType.Object ? null : token.Value<string>("$type");
    }
}

অতএব, আপনি বিশ্বব্যাপী এইভাবে অন্তর্ভুক্ত করতে পারেন:

public static JsonSerializerSettings StandardSerializerSettings => new JsonSerializerSettings
    {
        Converters = new List<JsonConverter>
        {
            new InterfaceConverter()
        }
    };

এই টোটেম এর উত্তর একটি বিস্তার। এটি মূলত একই জিনিস কিন্তু সম্পত্তি মিলিং সিরিয়ালাইজড জসন বস্তুর উপর ভিত্তি করে, .net বস্তুকে প্রতিফলিত করে না। যদি আপনি CamelCasePropertyNamesContractResolver ব্যবহার করে [JsonProperty] ব্যবহার করছেন, বা অন্য কোনও কাজ করছেন যা জ্যাসনকে .net বস্তুর সাথে মেলে না তবে এটি গুরুত্বপূর্ণ।

ব্যবহার সহজ:

[KnownType(typeof(B))]
public class A
{
   public string Name { get; set; }
}

public class B : A
{
   public string LastName { get; set; }
}

রূপান্তরকারী কোড:

/// <summary>
/// Use KnownType Attribute to match a divierd class based on the class given to the serilaizer
/// Selected class will be the first class to match all properties in the json object.
/// </summary>
public class KnownTypeConverter : JsonConverter {
    public override bool CanConvert( Type objectType ) {
        return System.Attribute.GetCustomAttributes( objectType ).Any( v => v is KnownTypeAttribute );
    }

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

    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
        System.Attribute[ ] attrs = System.Attribute.GetCustomAttributes( objectType );  // Reflection. 

        // check known types for a match. 
        foreach( var attr in attrs.OfType<KnownTypeAttribute>( ) ) {
            object target = Activator.CreateInstance( attr.Type );

            JObject jTest;
            using( var writer = new StringWriter( ) ) {
                using( var jsonWriter = new JsonTextWriter( writer ) ) {
                    serializer.Serialize( jsonWriter, target );
                    string json = writer.ToString( );
                    jTest = JObject.Parse( json );
                }
            }

            var jO = this.GetKeys( jObject ).Select( k => k.Key ).ToList( );
            var jT = this.GetKeys( jTest ).Select( k => k.Key ).ToList( );

            if( jO.Count == jT.Count && jO.Intersect( jT ).Count( ) == jO.Count ) {
                serializer.Populate( jObject.CreateReader( ), target );
                return target;
            }
        }

        throw new SerializationException( string.Format( "Could not convert base class {0}", objectType ) );
    }

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

    private IEnumerable<KeyValuePair<string, JToken>> GetKeys( JObject obj ) {
        var list = new List<KeyValuePair<string, JToken>>( );
        foreach( var t in obj ) {
            list.Add( t );
        }
        return list;
    }
}

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

ব্যবহার হিসাবে হিসাবে সহজ:

 string json = "{ Name:\"Something\", LastName:\"Otherthing\" }";
 var ret  = JsonConvert.DeserializeObject<A>(json, new KnownTypeConverter());

উপরের ক্ষেত্রে আবার টাইপ হবে।

JSON ক্লাস:

[KnownType(typeof(B))]
public class A
{
   public string Name { get; set; }
}

public class B : A
{
   public string LastName { get; set; }
}

রূপান্তরকারী কোড:

/// <summary>
    /// Use KnownType Attribute to match a divierd class based on the class given to the serilaizer
    /// Selected class will be the first class to match all properties in the json object.
    /// </summary>
    public  class KnownTypeConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return System.Attribute.GetCustomAttributes(objectType).Any(v => v is KnownTypeAttribute);
        }

        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
            System.Attribute[] attrs = System.Attribute.GetCustomAttributes(objectType);  // Reflection. 

                // Displaying output. 
            foreach (System.Attribute attr in attrs)
            {
                if (attr is KnownTypeAttribute)
                {
                    KnownTypeAttribute k = (KnownTypeAttribute) attr;
                    var props = k.Type.GetProperties();
                    bool found = true;
                    foreach (var f in jObject)
                    {
                        if (!props.Any(z => z.Name == f.Key))
                        {
                            found = false;
                            break;
                        }
                    }

                    if (found)
                    {
                        var target = Activator.CreateInstance(k.Type);
                        serializer.Populate(jObject.CreateReader(),target);
                        return target;
                    }
                }
            }
            throw new ObjectNotFoundException();


            // Populate the object properties

        }

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

টোটেমের পরিচিত ধরনের সমাধানের অন্যতম রূপ হিসাবে আপনি পরিচিত প্রকারের বৈশিষ্ট্যগুলি ব্যবহার করতে প্রয়োজনীয়তা এড়ানোর জন্য জেনেরিক টাইপ সমাধানকারী তৈরির প্রতিফলন ব্যবহার করতে পারেন।

এটি WCF এর জন্য জুভাল লোয়ের জেনেরিক রিসোলভারের মতো একটি কৌশল ব্যবহার করে।

যতক্ষণ আপনার বেস বর্গটি বিমূর্ত বা ইন্টারফেস থাকে ততক্ষণ জ্ঞাত প্রকারগুলি স্বয়ংক্রিয়ভাবে পরিচিত টাইপ বৈশিষ্ট্যগুলির সাথে সজ্জিত হওয়ার পরিবর্তে স্বয়ংক্রিয়ভাবে নির্ধারিত হবে।

আমার নিজের ক্ষেত্রে আমি সম্পত্তি থেকে এটি নির্ধারণ করার চেষ্টা করার পরিবর্তে আমার জসন বস্তুর টাইপ নির্ধারণ করতে একটি $ টাইপ সম্পত্তি ব্যবহার করতে পছন্দ করেছি, যদিও আপনি সম্পত্তি ভিত্তিক দৃঢ়সংকল্প ব্যবহার করতে এখানে অন্যান্য সমাধান থেকে ধার নিতে পারেন।

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

    public JsonKnownTypeConverter() : this(ReflectTypes())
    {

    }
    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 == x.Name));
        }
        else
        {
            return Activator.CreateInstance(objectType);
        }
        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();
    }

    //Static helpers
    static Assembly CallingAssembly = Assembly.GetCallingAssembly();

    static Type[] ReflectTypes()
    {
        List<Type> types = new List<Type>();
        var referencedAssemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
        foreach (var assemblyName in referencedAssemblies)
        {
            Assembly assembly = Assembly.Load(assemblyName);
            Type[] typesInReferencedAssembly = GetTypes(assembly);
            types.AddRange(typesInReferencedAssembly);
        }

        return types.ToArray();
    }

    static Type[] GetTypes(Assembly assembly, bool publicOnly = true)
    {
        Type[] allTypes = assembly.GetTypes();

        List<Type> types = new List<Type>();

        foreach (Type type in allTypes)
        {
            if (type.IsEnum == false &&
               type.IsInterface == false &&
               type.IsGenericTypeDefinition == false)
            {
                if (publicOnly == true && type.IsPublic == false)
                {
                    if (type.IsNested == false)
                    {
                        continue;
                    }
                    if (type.IsNestedPrivate == true)
                    {
                        continue;
                    }
                }
                types.Add(type);
            }
        }
        return types.ToArray();
    }

এটি একটি ফর্ম্যাট হিসাবে ইনস্টল করা যেতে পারে

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new JsonKnownTypeConverter());

স্ট্যান্ডার্ড CustomCreationConverter ব্যবহার করে, আমি কীভাবে সঠিক টাইপ ( Person বা Employee ) তৈরি করতে কাজ করতে সংগ্রাম করছিলাম, কারণ এটি নির্ধারণ করার জন্য আপনাকে JSON বিশ্লেষণ করতে হবে এবং Create পদ্ধতি ব্যবহার করে এটি Create করার কোনও উপায় নেই।

আমি রূপান্তর টাইপ সংক্রান্ত একটি আলোচনা থ্রেড খুঁজে পাওয়া যায় নি এবং এটি উত্তর প্রদান করা হয়েছে। এখানে একটি লিঙ্ক: রূপান্তর টাইপ করুন

JsonConverter , ReadJson পদ্ধতিকে অগ্রাহ্য করা এবং একটি নতুন বিমূর্ত Create যা একটি JObject গ্রহণ করে এমন পদ্ধতি Create

JObject ক্লাস একটি JSON অবজেক্ট লোড করার একটি উপায় সরবরাহ করে এবং এই বস্তুর মধ্যে থাকা ডেটা অ্যাক্সেস সরবরাহ করে।

ReadJson পদ্ধতি একটি JObject Create করে এবং JObject ইনস্ট্যান্সে পাস করে Create পদ্ধতি (আমাদের প্রাপ্ত রূপান্তরকারী শ্রেণী দ্বারা প্রয়োগ করা হয়)।

এই JObject উদাহরণ তারপর নির্দিষ্ট ক্ষেত্র অস্তিত্ব পরীক্ষা করে সঠিক টাইপ নির্ধারণ করতে বিশ্লেষণ করা যেতে পারে।

উদাহরণ

string json = "[{
        \"Department\": \"Department1\",
        \"JobTitle\": \"JobTitle1\",
        \"FirstName\": \"FirstName1\",
        \"LastName\": \"LastName1\"
    },{
        \"Department\": \"Department2\",
        \"JobTitle\": \"JobTitle2\",
        \"FirstName\": \"FirstName2\",
        \"LastName\": \"LastName2\"
    },
        {\"Skill\": \"Painter\",
        \"FirstName\": \"FirstName3\",
        \"LastName\": \"LastName3\"
    }]";

List<Person> persons = 
    JsonConvert.DeserializeObject<List<Person>>(json, new PersonConverter());

...

public class PersonConverter : JsonCreationConverter<Person>
{
    protected override Person Create(Type objectType, JObject jObject)
    {
        if (FieldExists("Skill", jObject))
        {
            return new Artist();
        }
        else if (FieldExists("Department", jObject))
        {
            return new Employee();
        }
        else
        {
            return new Person();
        }
    }

    private bool FieldExists(string fieldName, JObject jObject)
    {
        return jObject[fieldName] != null;
    }
}

public abstract class JsonCreationConverter<T> : JsonConverter
{
    /// <summary>
    /// Create an instance of objectType, based properties in the JSON object
    /// </summary>
    /// <param name="objectType">type of object expected</param>
    /// <param name="jObject">
    /// contents of JSON object that will be deserialized
    /// </param>
    /// <returns></returns>
    protected abstract T Create(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

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

    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
        T target = Create(objectType, jObject);

        // Populate the object properties
        serializer.Populate(jObject.CreateReader(), target);

        return target;
    }
}

JsonCreationConverter<T> জন্য উপরের সমাধানটি সমস্ত ইন্টারনেটে রয়েছে, তবে এটি এমন একটি ত্রুটি যা বিরল উপলক্ষ্যে নিজেকে প্রকাশ করে। রিডজসন পদ্ধতিতে তৈরি নতুন জসন রিডার মূল পাঠকের কনফিগারেশন মানগুলির (সংস্কৃতি, তারিখপার্শহ্যান্ডলিং, ডেটটাইমজোনহ্যান্ডলিং, ফ্লোটপার্সহ্যান্ডলিং, ইত্যাদি ...) কোনওও উত্তরাধিকারী নয়। সিরিয়ালাইজারে নতুন JsonReader ব্যবহার করার আগে এই মানগুলি অনুলিপি করা উচিত।

উপরেরটি বাস্তবায়নের কিছু সমস্যা সমাধানের জন্য আমি এটিকে সর্বোত্তম করে তুলতে পারি, কিন্তু এখনও মনে হচ্ছে কিছু জিনিস উপেক্ষা করা হচ্ছে:

আপডেট আমি একটি আরো স্পষ্ট পদ্ধতি আছে যে একটি বিদ্যমান পাঠক একটি কপি করে আপডেট। এই শুধু JsonReader সেটিংস ব্যক্তিগত অনুলিপি প্রক্রিয়া encapsulates। আদর্শভাবে এই ফাংশনটি নিউটনসফ্ট লাইব্রেরিতে নিজেই রক্ষণাবেক্ষণ করা হবে, তবে এখন জন্য, আপনি নিম্নলিখিতগুলি ব্যবহার করতে পারেন:

/// <summary>Creates a new reader for the specified jObject by copying the settings
/// from an existing reader.</summary>
/// <param name="reader">The reader whose settings should be copied.</param>
/// <param name="jObject">The jObject to create a new reader for.</param>
/// <returns>The new disposable reader.</returns>
public static JsonReader CopyReaderForObject(JsonReader reader, JObject jObject)
{
    JsonReader jObjectReader = jObject.CreateReader();
    jObjectReader.Culture = reader.Culture;
    jObjectReader.DateFormatString = reader.DateFormatString;
    jObjectReader.DateParseHandling = reader.DateParseHandling;
    jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
    jObjectReader.FloatParseHandling = reader.FloatParseHandling;
    jObjectReader.MaxDepth = reader.MaxDepth;
    jObjectReader.SupportMultipleContent = reader.SupportMultipleContent;
    return jObjectReader;
}

নিম্নরূপ এই ব্যবহার করা উচিত:

public override object ReadJson(JsonReader reader,
                                Type objectType,
                                object existingValue,
                                JsonSerializer serializer)
{
    if (reader.TokenType == JsonToken.Null)
        return null;
    // Load JObject from stream
    JObject jObject = JObject.Load(reader);
    // Create target object based on JObject
    T target = Create(objectType, jObject);
    // Populate the object properties
    using (JsonReader jObjectReader = CopyReaderForObject(reader, jObject))
    {
        serializer.Populate(jObjectReader, target);
    }
    return target;
}

পুরোনো সমাধান অনুসরণ করে:

/// <summary>Base Generic JSON Converter that can help quickly define converters for specific types by automatically
/// generating the CanConvert, ReadJson, and WriteJson methods, requiring the implementer only to define a strongly typed Create method.</summary>
public abstract class JsonCreationConverter<T> : JsonConverter
{
    /// <summary>Create an instance of objectType, based properties in the JSON object</summary>
    /// <param name="objectType">type of object expected</param>
    /// <param name="jObject">contents of JSON object that will be deserialized</param>
    protected abstract T Create(Type objectType, JObject jObject);

    /// <summary>Determines if this converted is designed to deserialization to objects of the specified type.</summary>
    /// <param name="objectType">The target type for deserialization.</param>
    /// <returns>True if the type is supported.</returns>
    public override bool CanConvert(Type objectType)
    {
        // FrameWork 4.5
        // return typeof(T).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
        // Otherwise
        return typeof(T).IsAssignableFrom(objectType);
    }

    /// <summary>Parses the json to the specified type.</summary>
    /// <param name="reader">Newtonsoft.Json.JsonReader</param>
    /// <param name="objectType">Target type.</param>
    /// <param name="existingValue">Ignored</param>
    /// <param name="serializer">Newtonsoft.Json.JsonSerializer to use.</param>
    /// <returns>Deserialized Object</returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        // Load JObject from stream
        JObject jObject = JObject.Load(reader);

        // Create target object based on JObject
        T target = Create(objectType, jObject);

        //Create a new reader for this jObject, and set all properties to match the original reader.
        JsonReader jObjectReader = jObject.CreateReader();
        jObjectReader.Culture = reader.Culture;
        jObjectReader.DateParseHandling = reader.DateParseHandling;
        jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
        jObjectReader.FloatParseHandling = reader.FloatParseHandling;

        // Populate the object properties
        serializer.Populate(jObjectReader, target);

        return target;
    }

    /// <summary>Serializes to the specified type</summary>
    /// <param name="writer">Newtonsoft.Json.JsonWriter</param>
    /// <param name="value">Object to serialize.</param>
    /// <param name="serializer">Newtonsoft.Json.JsonSerializer to use.</param>
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}




deserialization