0
{
    "Class1": {
        "Class2": [
            {"Name": "DerivedV1"},
            {"Name": "DerivedV2"},
            {"Name": "DerivedV3"}
        ]
    }
}

JsonConvert.DeserializeObject<Class1>(jsonString, settings);

public class Class1
{
    public List<BaseClass> DerivedClasses { get; set; }
}

public abstract BaseClass
{
    public string Name { get; set; }

    public abstract bool DoSomething;
}

public class DerivedV1 : BaseClass
{
    public override bool DoSomething()
    {
        // Logic here, different for each derived class.
    }
}

When trying to deserialize Class1, I can't figure out how to create the list of derived classed from name. I can't declare something like List BaseClass where BaseClass is abstract and I'm not sure how to use reflection during deserialization within Class2 to determine the derived class from the "name" value. I also looked into ICloneable but didn't get anywhere with it in this context.

Edit:

Here is what I ended up creating and calling from get and set

    public static List<T> DeserializeJObjectsToObjects<T>(IEnumerable<JObject> jObjects, string typeKey, string nameSpaceOfClass)
    {
        Assembly assembly = Assembly.GetExecutingAssembly();
        List<T> convert = new List<T>();

        foreach (var jObject in jObjects)
        {
            JToken typeName;
            jObject.TryGetValue(typeKey, out typeName);
            string fullNameSpace = string.Format(namespaceFormat, nameSpaceOfClass, typeName);
            Type t = Type.GetType(string.Format(fullNameSpace));
            convert.Add((T) Activator.CreateInstance(t));
        }

        return convert;
    }

    public static List<JObject> SerializeObjectsToJObjects<T>(IEnumerable<T> variableObjects )
    {
        List<JObject> convert = new List<JObject>();

        foreach (T variableObject in variableObjects)
        {
            var jsonString = JsonConvert.SerializeObject(variableObject);
            convert.Add(JObject.Parse(jsonString));
        }

        return convert;
    }
user2680142
  • 59
  • 1
  • 10

2 Answers2

0

First, some notes - I don't use JSonConvert, but there are articles that show you how to do this with. See Json.net serialize/deserialize derived types?, for example. However, you didn't include a json.net tag, so I'm assuming this should hopefully help, or at least point you to the right place.

I used the built in .Net JavaScriptSerializer. You may need to adjust this to work with your input. I created my input based on your code, and my json looks nothing like yours. So, you may still have some work left. I was able to get it working with a SimpleTypeResolver. Code below:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace WindowsFormsApplication6
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Class1 oClass = new Class1();
        DerivedV1 v1 = new DerivedV1();
        v1.Name = "DerivedV1";
        oClass.DerivedClasses.Add(v1);
        DerivedV2 v2 = new DerivedV2();
        v2.Name = "DerivedV2";
        oClass.DerivedClasses.Add(v2);
        DerivedV3 v3 = new DerivedV3();
        v3.Name = "DerivedV3";
        oClass.DerivedClasses.Add(v3);
        JavaScriptSerializer ser = new JavaScriptSerializer(new SimpleTypeResolver());
        string sSer = ser.Serialize(oClass);

        var test =ser.Deserialize(sSer,typeof(Class1));
        foreach (var tst in ((Class1)test).DerivedClasses)
        {
            Console.WriteLine(tst.Name + Environment.NewLine);
            Console.WriteLine(tst.GetType().ToString() + Environment.NewLine);
        }
    }
    public class Class1
    {
        public List<BaseClass> DerivedClasses { get; set; }
        public Class1()
        {
            DerivedClasses = new List<BaseClass>();
        }
    }
    public abstract class BaseClass
    {
        public string Name { get; set; }
        private bool _dosom;

        public abstract bool DoSomething();
        public BaseClass(){}
    }       
    public class DerivedV1 : BaseClass
    {
        public override bool DoSomething()
        {
            return true;
            // Logic here, different for each derived class.
        }
    }
    public class DerivedV2 : BaseClass
    {
        public override bool DoSomething()
        {
            return false;
            // Logic here, different for each derived class.
        }
    }
    public class DerivedV3 : BaseClass
    {
        public override bool DoSomething()
        {
            return true;
            // Logic here, different for each derived class.
        }
    }
}
}

my output json (with the SimpleTypeResolver):

{"__type":"WindowsFormsApplication6.Form1+Class1, WindowsFormsApplication6, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","DerivedClasses":[{"__type":"WindowsFormsApplication6.Form1+DerivedV1, WindowsFormsApplication6, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","Name":"DerivedV1"},{"__type":"WindowsFormsApplication6.Form1+DerivedV2, WindowsFormsApplication6, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","Name":"DerivedV2"},{"__type":"WindowsFormsApplication6.Form1+DerivedV3, WindowsFormsApplication6, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","Name":"DerivedV3"}]}

and without the type resolver, for comparison (causes errors):

{"DerivedClasses":[{"Name":"DerivedV1"},{"Name":"DerivedV2"},{"Name":"DerivedV3"}]}

And once I deserialize (in the for loop, Console.WriteLine)...

DerivedV1

WindowsFormsApplication6.Form1+DerivedV1

DerivedV2

WindowsFormsApplication6.Form1+DerivedV2

DerivedV3

WindowsFormsApplication6.Form1+DerivedV3 
Community
  • 1
  • 1
Aaron
  • 1,313
  • 16
  • 26
  • This wouldn't work if you deserialized straight from text as a user would input. When you use JavaScriptSerializer on the object you get additional info added such as "__type":"WindowsFormsApplication6.Form1+DerivedV2. You are right in certain cases but not exactly what I'm looking for. I want to make JSON template creation as easy on my user as possible. – user2680142 Jan 20 '17 at 22:39
  • Yes it needs to be serialized this way. He would need to have access to the json creation software, if not different. I believe if he can't generate the json,, he would need to use 1 class type, and use logic based on the name (or another attribute) to "DoSomething.". But to get derived types into a list of base type without something telling it what derived type? I'm pretty sure you'd have to code that yourself. Can you think of a way to do it with existing serializers? – Aaron Jan 20 '17 at 22:44
  • There isn't, I don't think. I haven't seen a way to specify a value to infer class type off of. The way I have this "working" right now, is that in BaseClass for the DervicedClasses property I have the getter and setter just manipulate a private list of JObjects created from the JSON. The getter uses Activator to create the correct instance, setter just serializes each element and sets the list. – user2680142 Jan 20 '17 at 22:52
  • I think you can write it.. I'll play around a little. It'll probably be pretty custom though. But I'm pretty sure I can make a "types" property where you pass in types. Will the name property always match the type name? – Aaron Jan 20 '17 at 23:35
  • I made a helper method but was thinking this would be an easy option. I use "Type" as a key in another branch of the JSON. Name here represents a module to call, Type in the other represents a resource such as VirtualMachine. – user2680142 Jan 20 '17 at 23:53
  • Yeah it sounds like you already did what I'd get you started on. Sorry my answer wasn't what you needed, my thinking was very acute - I usually wind up responsible for both sides so I got tunnel vision thinking you'd have control over the generation too. What you already have is pretty much your answer, I believe... – Aaron Jan 21 '17 at 00:09
  • Got me looking at a few more things, thanks for the input. – user2680142 Jan 21 '17 at 00:22
0

Using this as my solution.

  public static List<T> DeserializeJObjectsToObjects<T>(IEnumerable<JObject> jObjects, string typeKey, string nameSpaceOfClass)
    {
        Assembly assembly = Assembly.GetExecutingAssembly();
        List<T> convert = new List<T>();

        foreach (var jObject in jObjects)
        {
            JToken typeName;
            jObject.TryGetValue(typeKey, out typeName);
            string fullNameSpace = string.Format(nameSpaceFormat, nameSpaceOfClass, typeName);
            Type t = Type.GetType(string.Format(fullNameSpace));
            convert.Add((T) Activator.CreateInstance(t));
        }

        return convert;
    }

    public static List<JObject> SerializeObjectsToJObjects<T>(IEnumerable<T> variableObjects )
    {
        List<JObject> convert = new List<JObject>();

        foreach (T variableObject in variableObjects)
        {
            var jsonString = JsonConvert.SerializeObject(variableObject);
            convert.Add(JObject.Parse(jsonString));
        }

        return convert;
    }
user2680142
  • 59
  • 1
  • 10