Question37896661 custom JsonConverter

CSharp · Console

/Program.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Runtime.Serialization;
using System.Data;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Specialized;
using System.Web.Routing;
using System.Net.Mail;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Net;
using System.Runtime.Serialization.Formatters;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using System.Web.SessionState;

    public class OptionalType
    {
        public string setting1 { get; set; }
    }

    public class OptionalSettingsAttribute : System.Attribute
    {
        public OptionalSettingsAttribute()
        {
        }
    }

    [JsonConverter(typeof(StringEnumConverter))]
    public enum MyEnumTypes
    {
        //[EnumMember(Value = "simple1")] EnumMemberAttribute is not available on dotnetfiddle for some reason.
        Simple1,

        //[EnumMember(Value = "simple2")]
        Simple2,

        [OptionalSettingsAttribute]
        //[EnumMember(Value = "optional1")]
        Optional1,

        [OptionalSettingsAttribute]
        //[EnumMember(Value = "optional2")]
        Optional2
    }

    [JsonConverter(typeof(ConfigurationConverter))]
    public class Configuration
    {
        [JsonProperty(PropertyName = "name")]
        public string Name { get; set; }

        public MyEnumTypes Type { get; set; }

        public OptionalType TypeAdditionalData { get; set; }

        [JsonProperty(PropertyName = "value")]
        public int Value { get; set; }
    }

    class ConfigurationConverter : JsonConverter
    {
        const string typeName = "type";

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

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
                return null;
            var config = (existingValue as Configuration ?? (Configuration)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator());

            // Populate the regular property values.
            var obj = JObject.Load(reader);
            var type = obj.RemoveProperty(typeName);
            using (var subReader = obj.CreateReader())
                serializer.Populate(subReader, config);

            // Populate Type and OptionalType
            if (type is JValue) // Primitive value
            {
                config.Type = type.ToObject<MyEnumTypes>(serializer);
            }
            else
            {
                var dictionary = type.ToObject<Dictionary<MyEnumTypes, OptionalType>>(serializer);
                if (dictionary.Count > 0)
                {
                    config.Type = dictionary.Keys.First();
                    config.TypeAdditionalData = dictionary.Values.First();
                }
            }

            return config;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var config = (Configuration)value;
            var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(config.GetType());
            writer.WriteStartObject();
            foreach (var property in contract.Properties
                .Where(p => p.Writable && (p.ShouldSerialize == null || p.ShouldSerialize(config)) && !p.Ignored))
            {
                if (property.UnderlyingName == "Type" || property.UnderlyingName == "TypeAdditionalData")
                    continue;
                var propertyValue = property.ValueProvider.GetValue(config);
                if (propertyValue == null && serializer.NullValueHandling == NullValueHandling.Ignore)
                    continue;
                writer.WritePropertyName(property.PropertyName);
                serializer.Serialize(writer, propertyValue);
            }
            writer.WritePropertyName(typeName);
            if (config.Type.GetCustomAttributeOfEnum<OptionalSettingsAttribute>() == null)
            {
                serializer.Serialize(writer, config.Type);
            }
            else
            {
                var dictionary = new Dictionary<MyEnumTypes, OptionalType>
                {
                    { config.Type, config.TypeAdditionalData },
                };
                serializer.Serialize(writer, dictionary);
            }
            writer.WriteEndObject();
        }
    }

    public static class EnumExtensions
    {
        public static TAttribute GetCustomAttributeOfEnum<TAttribute>(this Enum value)
            where TAttribute : System.Attribute
        {
            var type = value.GetType();
            var memInfo = type.GetMember(value.ToString());
            return memInfo[0].GetCustomAttribute<TAttribute>();
        }
    }

    public static class JsonExtensions
    {
        public static JToken RemoveProperty(this JObject obj, string name)
        {
            if (obj == null)
                return null;
            var property = obj.Property(name);
            if (property == null)
                return null;
            var value = property.Value;
            property.Remove();
            property.Value = null;
            return value;
        }
    }
					
	public class Program
	{
        private static void Test(Configuration test, JsonSerializerSettings settings)
        {
            var json = JsonConvert.SerializeObject(test, Formatting.Indented, settings);

            Console.WriteLine("Serialized configuration:");
            Console.WriteLine(json);

            var testBack = JsonConvert.DeserializeObject<Configuration>(json, settings);

            Console.WriteLine("Re-serialized configuration:");
            var json2 = JsonConvert.SerializeObject(testBack, Formatting.Indented, settings);
            Console.WriteLine(json2);

            if (test.Type != testBack.Type)
            {
                throw new InvalidOperationException();
            }
            if (test.Type.GetCustomAttributeOfEnum<OptionalSettingsAttribute>() != null)
            {
                if (test.TypeAdditionalData != testBack.TypeAdditionalData && !JToken.DeepEquals(JToken.FromObject(test.TypeAdditionalData), JToken.FromObject(testBack.TypeAdditionalData)))
                {
                    throw new InvalidOperationException();
                }
            }
            if (test.Name != testBack.Name || test.Value != testBack.Value)
            {
                throw new InvalidOperationException();
            }
            Console.WriteLine("Configuration serialized and de-serialized successfully.");
            Console.WriteLine("");
        }

		public static void Main()
		{
            var test1 = new Configuration { Name = "simple", Value = 101, Type = MyEnumTypes.Simple1, TypeAdditionalData = null };
            var test2 = new Configuration { Name = "complex", Value = 202, Type = MyEnumTypes.Optional1, TypeAdditionalData = new OptionalType { setting1 = "optional1" } };
            var test3 = new Configuration { Name = "cpmplex", Value = 202, Type = MyEnumTypes.Optional2, TypeAdditionalData = null };

            Test(test1, null);
            Test(test2, null);
            Test(test3, null);
		}
	}
;