Deserialize JSON into Derived classes - c#

{
"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;
}

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

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;
}

Related

MakeGenericType() actually makes an object, and I cannot use the type's methods

What it boils down to is that I'm trying to make a Generic and while the type shows up correctly at runtime, during compile time its still object, and so I cannot use any of the generic type's methods.
thanks to brainless coder on a previous question I'm able to move forward a bit
dotnetfiddle
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var sample = new Baz<List<Foo>>();
sample.DoSomething();
}
public class Foo
{
}
public class Bar<T>
{
public void Boom()
{
}
}
public class Baz<T>
{
public void DoSomething(){
if (typeof(T).Name == "List`1")
{
var typeName = typeof(T).GetGenericArguments().Single().FullName;
var type = Type.GetType(typeName);
var genericRepoType = typeof(Bar<>);
var specificRepoType = genericRepoType.MakeGenericType(new Type[] { type });
var genericBar = Activator.CreateInstance(specificRepoType);
Console.WriteLine(genericBar.GetType().Name); // Shows Bar`1
// but at compile time its foo is still an object
genericBar.Boom();
//will error with 'object' does not contain a definition for Boom
}
}
}
}
This sounds like a very questionable design, but if you must, dynamic neatly solves your problem.
public static void Main() {
var sample = new Baz<List<Foo>>();
sample.DoSomething();
}
public class Foo { }
public class Bar<T> {
public void Boom() {
Console.WriteLine("I am booming");
}
}
public class Baz<T> {
public void DoSomething() {
var typeName = typeof(T).GetGenericArguments().Single().FullName;
var type = Type.GetType(typeName);
var genericRepoType = typeof(Bar<>);
var specificRepoType = genericRepoType.MakeGenericType(new Type[] { type });
dynamic genericBar = Activator.CreateInstance(specificRepoType);
Console.WriteLine(genericBar.GetType().Name);
genericBar.Boom();
}
}
https://dotnetfiddle.net/uPpfJa
Alternatively, you could declare an IBar interface.
public class Bar<T> : IBar {
public void Boom() {
Console.WriteLine("I am booming");
}
}
interface IBar {
void Boom();
}
...
var genericBar = (IBar)Activator.CreateInstance(specificRepoType);

Testing using RHinomock

I have a class to test which is tricky to test using Rhinomock unlike normal classes bacause its constructor is injected with a dependency which is not a single interface but an array of Interface objects. Please help me set up all stuff to write a test using rhinomock.
namespace ClinicalAdvantage.Domain.UserAppSettings
{
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
public class Agg : IAgg
{
private readonly ISource[] sources;
public Agg(ISource[] sources)
{
this.sources = sources;
}
public JObject GetAll()
{
var obj = new JObject();
foreach (var source in this.sources)
{
var token = source.GetCurr();
if (token != null)
{
obj.Add(new JProperty(source.Name, token));
}
}
return obj;
}
}
ISource is an interface which has 2 implementations. GetALL() iterates thro each implementated class object and calls the GetCurr method in each of the object and aggregates the result. I have to stub GetCurr method to return a standard Jtoken. I am unable to create a mock of this class Agg or a stub of ISource.
public interface ISource
{
string Name { get; }
bool Enabled { get; }
JToken GetCurr();
}
}
Something like this might work:
[TestClass]
public class AggTest
{
private ISource Isource;
private Agg agg;
[TestInitialize]
public void SetUp()
{
Isource = MockRepository.GenerateMock<ISource>();
agg = new Agg(new [Isource]);
}
[TestMethod]
public void GetAll()
{
Isource.Stub(x => x.GetCurr()).
Return(new JToken());
var jObject = agg.GetAll();
Assert.IsNotNull(jObject);
// Do your assertion that all JProperty objects are in the jObject
// I don't know the syntax
}
}

Inconsistent accessibility with protected internal member

Attempting to make a protected internal member of a protected internal class within a public class results with the following issue:
Inconsistent accessibility: field type
'what.Class1.ProtectedInternalClass' is less accessible than field
'what.Class1.SomeDataProvider.data'
The accessibility should be equivalent, as far as I know.
Where am I mistaken?
Origination class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace what
{
public class Class1
{
// This class cannot be modified, is only
// here to produce a complete example.
public class PublicClass
{
public PublicClass() { }
}
protected internal class ProtectedInternalClass : PublicClass
{
public ProtectedInternalClass() { }
public void SomeExtraFunction() { }
}
public class SomeDataProvider
{
public int AnInterestingValue;
public int AnotherInterestingValue;
protected internal ProtectedInternalClass data; //<--- Occurs here.
public PublicClass Data { get { return data; } }
}
public static SomeDataProvider RetrieveProvider()
{
SomeDataProvider provider = new SomeDataProvider();
provider.data = new ProtectedInternalClass();
provider.data.SomeExtraFunction();
return provider;
}
}
}
Verifying protected and internal properties, same assembly:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace what
{
public class Class2 : Class1
{
public Class2()
{
var pi = new ProtectedInternalClass();
var provider = new SomeDataProvider();
provider.data = pi;
}
// no errors here
}
public class Class3
{
public Class3()
{
var pi = new Class1.ProtectedInternalClass();
var provider = new Class1.SomeDataProvider();
provider.data = pi;
}
// no errors here
}
}
Verifying protected and internal properties, different assembly:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace some_other_assembly
{
public class Class4 : what.Class1
{
public Class4()
{
var pi = new ProtectedInternalClass();
var provider = new SomeDataProvider();
provider.data = pi;
}
// no errors here
}
public class Class5
{
public Class5()
{
var pi = new what.Class1.ProtectedInternalClass(); // <--- Inaccessible due to protection level, as it should be.
var provider = new what.Class1.SomeDataProvider();
provider.data = pi; // <--- Intellisense implies inaccessible, but not indicated via error.
}
}
}
The protected applies to different classes, and this can be seen with
class Derived : what.Class1.SomeDataProvider // note: Derived is not a nested class
{
public void f()
{
var data = this.data;
}
}
in a different assembly.
this.data has to be accessible, since the class derives from SomeDataProvider. Its type, ProtectedInternalClass, is not accessible, since the class does not derive from Class1.

I can't serialize a list of objects in C# with XmlSerializer

I have a list of many test implements the interface IDoTest, that I want to store in a file. I also want to read from this file.
It seemed natural to simple use the XmlSerializer to store the objects in my List of IDoTest. But when I do this I get a vague I am sorry I cant do that error in the neighborhood of System.Xml.Serialization.TypeDesc.CheckSupported()
Can the XmlSerializer only do trivial jobs? Or am I missing something? They are talking about custom serialization at MSDN.
Here is my simplified code example.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public interface IDoTest
{
void DoTest();
void Setup();
}
internal class TestDBConnection : IDoTest
{
public string DBName;
public void DoTest()
{
Console.WriteLine("DoHardComplicated Test");
}
public void Setup()
{
Console.WriteLine("SetUpDBTest");
}
}
internal class PingTest : IDoTest
{
public string ServerName;
public void DoTest()
{
Console.WriteLine("MaybeDoAPing");
}
public void Setup()
{
Console.WriteLine("SetupAPingTest");
}
}
internal class Program
{
private static void Main(string[] args)
{
TestDBConnection Do1 = new TestDBConnection { DBName = "SQLDB" };
PingTest Do2 = new PingTest { ServerName = "AccTestServ_5" };
List<IDoTest> allTest = new List<IDoTest> { Do1, (Do2) };
// Now I want to serialize my list.
// Its here where I get the error at allTest
XmlSerializer x = new XmlSerializer(allTest.GetType());
StreamWriter writer = new StreamWriter("mySerializedTestSuite.xml");
x.Serialize(writer, allTest);
}
}
}
XmlSerializer cannot serialize an interface, and by extension, it cannot serialize a List<> of some interface. It can only serialize concrete object types.
The assumption is that you will probably want to deserialize the objects at some point, and if it only output information pertaining to that interface, it can't guarantee that all the necessary data is present to reconstruct the original objects.
This post shows a potential workaround, if you are able to use an abstract base class and explicitly provide every possible type of object that may appear in the list.
I followed the link that StriplingWarrior gave and found this excellent answer. https://stackoverflow.com/a/15089253/648076 from webturner
I changed his implementation and made a class class ListOfToDo that implemented both List and IXmlSerializable. That worked!
Here is my changed code.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public interface IDoTest
{
void DoTest();
void Setup();
}
public class TestDBConnection : IDoTest
{
public string DBName;
public void DoTest()
{
Console.WriteLine("DoHardComplicated Test");
}
public void Setup()
{
Console.WriteLine("SetUpDBTest");
}
}
public class PingTest : IDoTest
{
public string ServerName;
public void DoTest()
{
Console.WriteLine("MaybeDoAPing");
}
public void Setup()
{
Console.WriteLine("SetupAPingTest");
}
}
public class ListOfToDo : List<IDoTest>, **IXmlSerializable**
{
#region IXmlSerializable
public XmlSchema GetSchema(){ return null; }
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement("ListOfToDo");
while (reader.IsStartElement("IDoTest"))
{
Type type = Type.GetType(reader.GetAttribute("AssemblyQualifiedName"));
XmlSerializer serial = new XmlSerializer(type);
reader.ReadStartElement("IDoTest");
this.Add((IDoTest)serial.Deserialize(reader));
reader.ReadEndElement(); //IDoTest
}
reader.ReadEndElement(); //IDoTest
}
public void WriteXml(XmlWriter writer)
{
foreach (IDoTest test in this)
{
writer.WriteStartElement("IDoTest");
writer.WriteAttributeString("AssemblyQualifiedName", test.GetType().AssemblyQualifiedName);
XmlSerializer xmlSerializer = new XmlSerializer(test.GetType());
xmlSerializer.Serialize(writer, test);
writer.WriteEndElement();
}
}
#endregion
}
internal class Program
{
private static void Main(string[] args)
{
TestDBConnection Do1 = new TestDBConnection { DBName = "SQLDB" };
PingTest Do2 = new PingTest { ServerName = "AccTestServ_5" };
ListOfToDo allTest = new ListOfToDo { Do1, (Do2) };
// Now I want to serialize my list.
// Its here where I get the error at allTest
XmlSerializer x = new XmlSerializer(allTest.GetType());
StreamWriter writer = new StreamWriter("mySerializedTestSuite.xml");
x.Serialize(writer, allTest);
writer.Flush();
writer.Close();
//Read it aka deserialize
{
var xmlSerializer = new XmlSerializer(typeof(ListOfToDo));
var xmlReader = XmlReader.Create(new StreamReader("mySerializedTestSuite.xml"));
ListOfToDo readWhatToTest = (ListOfToDo)xmlSerializer.Deserialize(xmlReader);
xmlReader.Close();
}
}
}
}
Output will then be:
<?xml version="1.0" encoding="utf-8"?>
<ListOfToDo>
<IDoTest AssemblyQualifiedName="ConsoleApplication1.TestDBConnection, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<TestDBConnection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DBName>SQLDB</DBName>
</TestDBConnection>
</IDoTest>
<IDoTest AssemblyQualifiedName="ConsoleApplication1.PingTest, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<PingTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ServerName>AccTestServ_5</ServerName>
</PingTest>
</IDoTest>
</ListOfToDo>
Not sure if this might be the cause of your issue but on these two examples they do use typeof(T) instead of T.GetType()
http://msdn.microsoft.com/en-us/library/71s92ee1.aspx
I can't serialize a list of objects in C# with XmlSerializer

C# Instantiate Class from String given an Interface

I am trying to make an instance of a class based on a string that will be retrieved from the User Interface, and then I want to access the properties of the instance of the class.
Here is an overview of what I have so far -
namespace MamdaAdapter
{
public interface IExchange
{
string GetTransport();
}
}
namespace MamdaAdapter
{
public class Exchange
{
public class Arca : IExchange
{
private const string _Transport = "tportname";
public string GetTransport()
{
return _Transport;
}
}
public static IExchange DeriveExchange(string ExchangeName)
{
IExchange SelectedExchange = (IExchange)Activator.CreateInstance(Type.GetType(ExchangeName));
return SelectedExchange;
}
}
}
namespace MyUserInterface
{
public class MainForm
{
private void simpleButton1_Click(object sender, EventArgs e)
{
IExchange SelectedExchange = Exchange.DeriveExchange("Exchange.Arca");
Console.WriteLine(SelectedExchange.GetTransport());
}
}
}
UPDATE:
Right now, I'm getting an Exception that says the "Value cannot be null" which to me means that it is unable to create the instance of the class given the string provided -
The problem here is how you specify the name of your class:
First, specify the namespace. Second, since Arca is an inner class you must use '+' instead of '.'
(...) = Exchange.DeriveExchange("MamdaAdapter.Exchange+Arca");
Assuming you UI doesnt expose the full type name, you typically want a dictionary to associate the display name to the type:
Dictionary<string, Type> _associations = new Dictionary<string, Type>();
Then, you simply instantiate the new object:
if(_associations.ContainsKey(someString))
{
Type selectedType = _associations[someString];
return Activator.CreateInstance(selectedType) as IExchange;
}
throw new ApplicationException("No type defined for that string yo");
If the string is not known at compile time, you basically need to check for the existance of the type:
var type = Type.GetType(someString);
if(type != null)
{
// Do Stuff
}
I wrote a small c# console application to simulate your need, tested ok, hope it helps:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MamdaAdapter;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
IExchange SelectedExchange = Exchange.DeriveExchange("MamdaAdapter.Arca");
Console.WriteLine(SelectedExchange.GetTransport());
}
}
}
namespace MamdaAdapter
{
public interface IExchange
{
string GetTransport();
}
}
namespace MamdaAdapter
{
public class Arca : IExchange
{
private const string _Transport = "tportname";
public string GetTransport()
{
return _Transport;
}
}
}
namespace MamdaAdapter
{
public class Exchange
{
public static IExchange DeriveExchange(string ExchangeName)
{
IExchange SelectedExchange = (IExchange)Assembly.GetAssembly(typeof(IExchange)).CreateInstance(ExchangeName, false, BindingFlags.CreateInstance, null, null, null, null);
return SelectedExchange;
}
}
}
If the Type you are looking for is not defined in the same assembly that is executing Type.GetType you must use the AssemblyQualifiedName (something like MyNamespace.MyClass, MyAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089), even the FullName is not enough. Otherwise you could first get the assembly containing the class and then execute the GetType method of the Assembly class.

Categories