CSharp · Console
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);
}
}