I am working on complex data transfer for Web API. it suppose to send structure that has a collection of classes with nested polymorphic properties. The default JSON Converter doesn't recognize polymorphic structures by default. I tried to use the full namespace resolution. Unfortunately, in my scenario it didn't work as i have different frameworks (Full framework and .net core) on the client server sides and it doesn't resolve it properly namespaces and assemblies. (yes I tried mapping of Assemblies and Namespaces and realized that it is gonna be unsupportable mess)
I think custom converter should make it work, unfortunately I have no much knowledge of how to write it properly.
The data structure is like following:
I have a main context with some properties and a Collection of Containers. Each Container has the polymorphic properties that at the serializing should be resolved with extra parameter 'type' that contains the class name
Issue:
I fixed all the issues in this sample , the code is completely working
public class MyConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteStartArray();
var t = value as List<Container>;
foreach (var e in t)
{
var container = new JObject();
var fi = e.PropertyA;
JObject o = JObject.FromObject(fi);
o.AddFirst(new JProperty("type", new JValue(fi.GetType().Name)));
container.Add(new JProperty("PropertyA", o));
var res = e.PropertyB;
o = JObject.FromObject(res);
o.AddFirst(new JProperty("type", new JValue(res.GetType().Name)));
container.Add(new JProperty("PropertyB", o));
container.WriteTo(writer);
}
writer.WriteEndArray();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return string.Empty;
}
else if (reader.TokenType == JsonToken.String)
{
return serializer.Deserialize(reader, objectType);
}
else
{
JArray array = JArray.Load(reader);
List<Container> collection = new List<Container>();
foreach (var item in array.Children())
{
JToken propATkn = item.First;
JToken propBTkn = propATkn.Next;
var strType = ((JProperty)(propATkn.OfType<JObject>()).First().First).Value.ToString();
Type type = Type.GetType(strType);
BaseClassA propA = propATkn.OfType<JObject>().FirstOrDefault().ToObject(type) as BaseClassA;
strType = ((JProperty)(propBTkn.OfType<JObject>()).First().First).Value.ToString();
type = Type.GetType(strType);
BaseClassB propB = propBTkn.OfType<JObject>().FirstOrDefault().ToObject(type) as BaseClassB;
collection.Add(new Container { PropertyA = propA, PropertyB = propB});
}
return collection;
}
}
public override bool CanWrite
{
get { return true; }
}
public override bool CanConvert(Type objectType)
{
return true;
}
}
public class Context
{
[JsonConverter(typeof(MyConverter))]
public IList<Container> sources { get; set; } = new List<Container>();
public int Value { get; set; }
public string Key { get; set; }
}
public class Container
{
public BaseClassA PropertyA { get; set; }
public BaseClassB PropertyB { get; set; }
}
public class BaseClassB
{
public int B;
}
public class ClassBA : BaseClassB
{
public int BA;
}
public class ClassBB : BaseClassB
{
public int BB;
}
public class BaseClassA
{
public int A;
}
public class ClassAA : BaseClassA
{
public int AA;
}
public class ClassAB : BaseClassA
{
public int AB;
}