CSharp · Console
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using NUnit.Framework; // Throws NUnit.Framework.AssertionException
// https://stackoverflow.com/questions/38357864/c-sharp-how-to-conditionally-deserialize-json-object-based-on-another-json-pro
internal class IntToBoolConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((bool)value) ? "1" : "0");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var token = JToken.Load(reader);
if (token.Type == JTokenType.Boolean)
return (bool)token;
var val = (int)token;
return val != 0;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(bool) || objectType == typeof(bool?);
}
}
public class ActionTypeConverter : StringEnumConverter
{
public ActionTypeConverter()
: base()
{
this.CamelCaseText = true;
}
}
public abstract class ActionResult
{
}
// Add subtypes of ActionResult corresponding to each different type of result.
public class OpenActionResult : ActionResult
{
public string property1 { get; set; }
public string property2 { get; set; }
public DateTime? property3 { get; set; }
public string property4 { get; set; }
public bool property5 { get; set; }
}
[JsonConverter(typeof(ActionConverter))]
public class Action
{
readonly static Dictionary<Type, System.Type> typeToSystemType;
readonly static Dictionary<System.Type, Type> systemTypeToType;
static Action()
{
typeToSystemType = new Dictionary<Type, System.Type>
{
{ Type.Open, typeof(OpenActionResult) },
// Add additional dictionary entries corresponding to each different subtype of ActionResult
};
systemTypeToType = typeToSystemType.ToDictionary(p => p.Value, p => p.Key);
}
public static Type SystemTypeToType(System.Type systemType)
{
return systemTypeToType[systemType];
}
public static System.Type TypeToSystemType(Type type)
{
return typeToSystemType[type];
}
// Add enum values for Type corresponding to each different subtype of ActionResult
public enum Type
{
Open,
Close,
Remove,
Delete,
Reverse,
Alert,
ScaleInOut,
Nothing
}
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("active")]
[JsonConverter(typeof(IntToBoolConverter))]
public bool Active { get; set; }
[JsonProperty("type")]
[JsonConverter(typeof(ActionTypeConverter))]
public Type ActionType { get; set; }
[JsonProperty("result")]
public ActionResult Result { get; set; }
}
class ActionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var obj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var action = existingValue as Action ?? (Action)contract.DefaultCreator();
// Remove the Result property for manual deserialization
var result = obj.GetValue("Result", StringComparison.OrdinalIgnoreCase).RemoveFromLowestPossibleParent();
// Populate the remaining properties.
using (var subReader = obj.CreateReader())
{
serializer.Populate(subReader, action);
}
// Process the Result property
if (result != null)
action.Result = (ActionResult)result.ToObject(Action.TypeToSystemType(action.ActionType));
return action;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public static class JsonExtensions
{
public static JToken RemoveFromLowestPossibleParent(this JToken node)
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
}
class TestClass
{
static string GetJson()
{
var json = @"{
""name"":""test1"",
""id"":""aa0832f0508bb580ce7f0506132c1c13"",
""active"":""1"",
""type"":""open"",
""result"":{
""property1"":""buy"",
""property2"":""123.123"",
""property3"":""2016-07-16T23:00:00"",
""property4"":""768"",
""property5"":true
}
}";
return json;
}
public static void Test()
{
var json = GetJson();
Console.WriteLine("Original JSON: ");
Console.WriteLine(json);
var action = JsonConvert.DeserializeObject<Action>(json);
var json2 = JsonConvert.SerializeObject(action, Formatting.Indented);
Console.WriteLine("Re-serialized JSON: ");
Console.WriteLine(json2);
if (JToken.DeepEquals(JToken.Parse(json), JToken.Parse(json2)))
{
Console.WriteLine("Original and re-serialized JSON are equivalent.");
}
else
{
throw new InvalidOperationException("Original and re-serialized JSON are NOT equivalent.");
}
var action2 = JsonConvert.DeserializeObject<Action>(json2);
var json3 = JsonConvert.SerializeObject(action, Formatting.Indented);
Console.WriteLine("Re-re-serialized JSON: ");
Console.WriteLine(json3);
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Namespace, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine();
try
{
TestClass.Test();
}
catch (Exception ex)
{
Console.WriteLine("Failed with unhandled exception: ");
Console.WriteLine(ex);
throw;
}
}
}