0

Consider next code snippet:

    static void Main(string[] args)
    {
        var classes = new Classes()
        {
            Instances = new A[]{
                new B
                {
                    BirthDate = DateTime.Now,
                    Name = "B1",
                    SomethingElse = "Test"
                },
                new C
                {
                    Name = "C1",
                    SomethingElse1 = "Test2",
                    SomethingElse2 = "Test3",
                }
            }
        };
        var serialized = JsonConvert.SerializeObject(classes);
        var deserialized = JsonConvert.DeserializeObject<Classes>(serialized);
    }
}

public class Classes
{
    public A[] Instances { get; set; }
}

public enum ClassType
{
    B = 1,
    C = 2
}

public class A
{
    public string Name { get; set; }
    public virtual ClassType ClassType { get; }
}

public class B : A
{
    public string SomethingElse { get; set; }
    public DateTime BirthDate { get; set; }
    public override ClassType ClassType => ClassType.B;

}

public class C : A
{
    public string SomethingElse1 { get; set; }
    public string SomethingElse2 { get; set; }
    public override ClassType ClassType => ClassType.C;
}

I need to inject my own logic into the process how deserializer handle classes with inheritance. In that case I want to make decision based on ClassType property in JSON. Any ideas/hints how to do it?

BTW. I know that I can use feature of newtonsoft.json TypeNameHandling = TypeNameHandling.All, however I cannot control serialization process since the data is sent from 3rd party system. The only thing that I have control over is deserialization part.

Maris
  • 4,429
  • 5
  • 33
  • 62
  • It's pretty easy to write a custom converter https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm – DavidG Nov 27 '17 at 11:52

2 Answers2

1

Since you can't use TypeNameHandling, you'd have to parse it first, find the type, and then deserialize.

Like so:

var jObj = Newtonsoft.Json.Linq.JObject.Parse(serialized);

var instances = jObj["Instances"].AsJEnumerable();
var myCol = new List<A>();
myCol.AddRange(instances.Select(x => (x["ClassType"] as JToken)
.ToObject<ClassType>() == ClassType.B ?
  (x as JObject).ToObject<B>() : 
  (x as JObject).ToObject<C>());
zaitsman
  • 7,571
  • 4
  • 35
  • 64
  • Is it an implementation of ReadJson method? – Maris Nov 27 '17 at 12:52
  • @Maris personally i wouldn't bother with the converter, but it depends on how you structure the code. As i can see, you've used it in `ReadJson`. – zaitsman Nov 27 '17 at 22:32
0

Thanks to @zaitsman and @DavidG I came-up with solution that works for me. Here it is:

public class AClassConverter : JsonConverter
{
    private readonly Type[] _types;

    public AClassConverter(params Type[] types)
    {
        _types = types;
    }

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        var jObj = JObject.Load(reader);
        var classType = jObj["ClassType"].ToObject<ClassType>();
        return classType == ClassType.B ? 
            (A)jObj.ToObject<B>() : 
            (A)jObj.ToObject<C>();
    }

    public override bool CanRead => true;

    public override bool CanWrite => false;

    public override bool CanConvert(Type objectType)
    {
        return _types.Any(t => t == objectType);
    }
}

and while deserializing:

var deserialized = JsonConvert.DeserializeObject<Classes>(serialized, new AClassConverter(typeof(A)));
Maris
  • 4,429
  • 5
  • 33
  • 62
  • just note that `ReadAsInt32` may fail you -> if you are actually dealing with the model you've supplied, enums can be serialized as both `string` and `int`. The solution i provided dealt with that (i tried in c# interactive window). – zaitsman Nov 27 '17 at 22:33
  • @zaitsman , hmm, you are right. Will fix it, thank you! – Maris Nov 28 '17 at 08:25