0

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;
}
Alex G
  • 595
  • 6
  • 21
  • Why not use `TypeNameHandling.Auto` as shown in [JSON.NET - how to deserialize collection of interface-instances?](https://stackoverflow.com/a/15881570/3744182)? Note you will need to write a custom `ISerializationBinder` as discussed [here](https://stackoverflow.com/q/39565954/3744182) and [here](https://stackoverflow.com/q/49038055/3744182) but that should still be easier that writing all those converters. – dbc Apr 10 '18 at 17:07
  • 1
    Though if you really want to do this with converters you could start with [Can I serialize nested properties to my class in one operation with Json.net?](https://stackoverflow.com/a/30179162/3744182) and [How to conditionally deserialize JSON object based on another JSON property?](https://stackoverflow.com/q/38357864/3744182). – dbc Apr 10 '18 at 17:12
  • I have seen these links and tried to following with this solution, in my scenario I have different frameworks (full framework and .net core) on client / server sides. and it involves mapping namespaces and assemblies which is completely makes mess.... – Alex G Apr 10 '18 at 17:13
  • 1
    A custom serialization binder should do the job then because the serialization binder gets to specify the namespaces and assembly names. See [Custom SerializationBinder](https://www.newtonsoft.com/json/help/html/SerializeSerializationBinder.htm) for documentation. But see also [How to deserialize json objects into specific subclasses?](https://stackoverflow.com/q/36584071/3744182) which demonstrates processing of the `"$type"` property inside `JsonConverter.ReadJson()`. You might be able to use that as a template. – dbc Apr 10 '18 at 17:15

0 Answers0