I have such classes
class Holder { public int ObjType { get; set; } public List<Base> Objects { get; set; } } abstract class Base { // ... doesn't matter } class DerivedType1 : Base { // ... doesn't matter } class DerivedType2 : Base { // ... doesn't matter }
Using WebAPI I want to receive the object Holder
and deserialize it correctly. Based on the ObjType
value I need Objects
property to be deserialized either as List<DerivedType1>
(ObjType == 1
) or List<DerivedType2>
(ObjType == 2
).
At the moment I searched SO and internet for best approach, but the best I've found is this answer https://stackoverflow.com/a/8031283/1038496. The problem of this solution is, that it loses context of parent object, so I cannot find out the value of ObjType
. OK, I could solve it by creating custom JsonConverter
for Holder
and remebering the ObjType
value, but still I'm afraid of this line:
serializer.Populate(jObject.CreateReader(), target);
as the comment below this answer says:
The new JsonReader created in the ReadJson method does not inherit any of the original reader's configuration values (Culture, DateParseHandling, DateTimeZoneHandling, FloatParseHandling, etc...). These values should be copied over before using the new JsonReader in serializer.Populate().
which is a problem for me and copying these values by myself doesn't seem clean to me (what if I miss something?)
So the question is: Is there a better approach I've missed to deserialize abstract object property based on parent property value?
A common way to deserialize JSON is to first create a class with properties and fields that represent one or more of the JSON properties. Then, to deserialize from a string or a file, call the JsonSerializer. Deserialize method.
Serialization and deserialization in . NET application, JSON data format conversion to . NET objects and vice versa is very common. Serialization is the process of converting . NET objects such as strings into a JSON format and deserialization is the process of converting JSON data into . NET objects.
Provides methods for converting between . NET types and JSON types. Newtonsoft.Json.
Abstract classes are similar to interfaces. You cannot instantiate them, and they may contain a mix of methods declared with or without an implementation. However, with abstract classes, you can declare fields that are not static and final, and define public, protected, and private concrete methods.
You are on the right track. You do need to implement a custom JsonConverter
for your Holder
class to handle this situation, as you suggested. But, don't worry, it is possible to write the converter in such a way that you can use the original reader and serializer instances passed to the converter, without ever needing to copy the settings over to new instances. Here is how I would write it:
class HolderConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(Holder)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject jo = JObject.Load(reader); Holder holder = new Holder(); holder.ObjType = (int)jo["ObjType"]; holder.Objects = new List<Base>(); foreach (JObject obj in jo["Objects"]) { if (holder.ObjType == 1) holder.Objects.Add(obj.ToObject<DerivedType1>(serializer)); else holder.Objects.Add(obj.ToObject<DerivedType2>(serializer)); } return holder; } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }
Here is a quick demo:
class Program { static void Main(string[] args) { string json = @" [ { ""ObjType"" : 1, ""Objects"" : [ { ""Id"" : 1, ""Foo"" : ""One"" }, { ""Id"" : 2, ""Foo"" : ""Two"" }, ] }, { ""ObjType"" : 2, ""Objects"" : [ { ""Id"" : 3, ""Bar"" : ""Three"" }, { ""Id"" : 4, ""Bar"" : ""Four"" }, ] }, ]"; List<Holder> list = JsonConvert.DeserializeObject<List<Holder>>(json); foreach (Holder holder in list) { if (holder.ObjType == 1) { foreach (DerivedType1 obj in holder.Objects) { Console.WriteLine("Id: " + obj.Id + " Foo: " + obj.Foo); } } else { foreach (DerivedType2 obj in holder.Objects) { Console.WriteLine("Id: " + obj.Id + " Bar: " + obj.Bar); } } } } } [JsonConverter(typeof(HolderConverter))] class Holder { public int ObjType { get; set; } public List<Base> Objects { get; set; } } abstract class Base { public int Id { get; set; } } class DerivedType1 : Base { public string Foo { get; set; } } class DerivedType2 : Base { public string Bar { get; set; } }
Output:
Id: 1 Foo: One Id: 2 Foo: Two Id: 3 Bar: Three Id: 4 Bar: Four
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With