I need to deserialize a JSON string into a type which is not know at compile time. There are several classes that it can be deserialized into. The name of the class is provided as input into the application and based on that I want to instantiate the class (already done this through reflection):
var type = Type.GetType(className);
var myClassInstance = (IParser)Activator.CreateInstance(type);
...and then use its type as the generic type parameter for JsonConvert.DeserializeObject<typeof(myClassInstance).Name>(jsonString) but that doesn't work.
How can I provide the class to DeserializeObject<>() dynamically?
Instead of using an generic method overload like JsonConvert.DeserializeObject<T>(String) and having to resort to reflection as some comments state, you could simply use the non generic counterpart JsonConvert.DeserializeObject(String, Type), which just takes in a Type instance like you already have!
Implementation
Initialization
var class1s = new Class1() {
ID = 1, Name = "Test", Comment = "This Code is Tested!."
};
var class2s = new Class2() {
xVal1 = 1, XVal2 = 5, xval3 = 10
};
var JSON1 = Newtonsoft.Json.JsonConvert.SerializeObject(class1s);
var JSON2 = Newtonsoft.Json.JsonConvert.SerializeObject(class2s);
Calling Functions
var classname1 = typeof(Class1).FullName;
var type1 = Type.GetType(classname1);
var classname2 = typeof(Class2).FullName;
var type2 = Type.GetType(classname2);
var c = LocalConverter(JSON1, type1);
var c2 = LocalConverter(JSON2, type2);
Class Models
public class Class1 {
public int ID {
get;
set;
}
public string Name {
get;
set;
}
public string Comment {
get;
set;
}
}
public class Class2 {
public int xVal1 {
get;
set;
}
public int XVal2 {
get;
set;
}
public int xval3 {
get;
set;
}
}
Required Method
private object LocalConverter(string o, Type xtype) {
return Newtonsoft.Json.JsonConvert.DeserializeObject(o, xtype);
}
Related
This question already has answers here:
Elegant initialization of an array of class instances in C# [closed]
(3 answers)
Constructor for an array of class objects
(2 answers)
Closed 1 year ago.
Hi I have this code not sure how I can set the value of Value and Value1
public class testclass {
public ID[] test { get; set; }
public class ID
{
[JsonPropertyName("ID")]
public string Value { get; set; }
public string Value1 { get; set; }
}
}
e.g. var a = new testclass ();
a.test = ????
The fastest way with your existing code is:
a.test = new testclass.ID[] { new testclass.ID() { Value = "test", Value1 = "test1" } };
But some refactoring might help you a bit. Here's a fully compiling example.
Constructors were added to make the syntax cleaner. Rather than using the syntax of new ID { Value = "", Value1 = "" }; you can use new ID("", "");. Note that since you're doing serialization/deserialization, you may need to explicitly include a parameterless constructor, as it's unclear from your small code sample how this might get handled.
And the ID class was separated out, no longer 'underneath' the testclass class.
using System.Text.Json.Serialization; // from System.Text.Json library
namespace SomeNamespace
{
public class Program
{
static void Main()
{
var id = new ID("hello", "world");
var a = new testclass(id);
}
}
public class testclass
{
public ID[] test { get; set; }
public testclass() { /* might need parameterless for serialization */ }
// just makes an array with one entry
public testclass(ID id)
{
test = new ID[] { id };
}
}
// separate ID class from 'testclass'
public class ID
{
[JsonPropertyName("ID")]
public string Value { get; set; }
public string Value1 { get; set; }
public ID() { /* might need parameterless for serialization */ }
public ID(string value, string value1)
{
Value = value;
Value1 = value1;
}
}
}
This is what you're looking for:
var a = new testclass ();
a.test = new testclass.ID[]{};
You can also use object initialization like this:
a.test = new testclass.ID[]{
new testclass.ID(){Value = "1", Value1 = "2"},
new testclass.ID(){Value = "AnotherValue", Value1 = "10"}
};
And to read individual values:
a.test[0].Value == "1"
a.test[1].Value1 == "10"
Though these kind of nested classes are not recommended for most cases, it should work.
I have a class with 2 properties
public class SampleClass
{
public string Name { get; set; }
public List<Component> Components { get; set; }
}
And another class which is hold some string properties.
public class Component
{
public string Name { get; set; }
public string Age{ get; set; }
}
I have instance of this class created and added into a List
SampleClass classWithValues = new SampleClass();
var listComponent = new List<Component>();
listComponent.add(new Component{Name = "Random string",Age = "31"})
classWithValues.Components = listComponent;
classWithValues.Name = "TestName"
var listWithObjectClass = new List<SampleClass>();
listWithObjectClass.add(classWithValues);
Then i made a new instance of the SampleClass class and add exactly the same value into the properties :
SampleClass classWithValues1 = new SampleClass();
var listComponent1 = new List<Component>();
listComponent1.add(new Component{Name = "Random string",Age = "31"})
classWithValues1.Components = listComponent1;
classWithValues1.Name = "TestName";
And here is coming the strange part :
if I compare the property Names inside the list with the second instance of the Sample class with the new instance of the same class:
bool alreadyExists = listWithObjectClass.Any(x => x.Name == classWithValues1 .Name);
the result is true BUT
if I compare the List properties
bool alreadyExists = listWithObjectClass.Any(x => x.Components == classWithValues1.Components);
the result is false ?!
Can someone please give some information about this behavior.
Sorry my first answer was not quite right...
In order to get alreadyExist to be true you need to put in place property comparison in your classes as otherwise the equality comparison performed is the default reference comparison. Your objects contains the same property values but are actually different instances... The default equality comparison for objects is comparing references not content.
Try this...
void Main()
{
SampleClass classWithValues = new SampleClass();
var listComponent = new Components();
listComponent.Add(new Component{Name = "Random string",Age = "31"});
classWithValues.Components = listComponent;
classWithValues.Name = "TestName";
var listWithObjectClass = new List<SampleClass>();
listWithObjectClass.Add(classWithValues);
SampleClass classWithValues1 = new SampleClass();
var listComponent1 = new Components();
listComponent1.Add(new Component{Name = "Random string",Age = "31"});
classWithValues1.Components = listComponent1;
classWithValues1.Name = "TestName";
bool alreadyExists = listWithObjectClass.Any(x => x.Components.Equals(classWithValues1.Components));
}
public class SampleClass
{
public string Name { get; set; }
public Components Components { get; set; }
}
public class Component : IEquatable<Component>
{
public string Name { get; set; }
public string Age{ get; set; }
public bool Equals(Component otherComponent)
{
return Name == otherComponent.Name && Age == otherComponent.Age;
}
}
public class Components :List<Component>, IEquatable<Components>
{
public bool Equals(Components otherComponents)
{
if(this.Count!= otherComponents.Count) return false;
return this.TrueForAll(a=> otherComponents.Any(q=>q.Equals(a)))
&& otherComponents.TrueForAll(a=> this.Any(q=>q.Equals(a)));
}
}
The first comparison is about comparing the value of the two string. However, the second comparison is about Comparing two different object which their reference are different. Indeed, for the second comparison, compare their hashCode. To watch this, you can call .GetHashCode() for these two objects.
listComponent.GetHashCode() == listComponent1.GetHashCode() // false
listComponent[0].GetHashCode() == listComponent1[0].GetHashCode() // false
I have a class structure below. I am getting this error. Am i missing something here?
Object does not match target type.
Class Structure
public class Schedule
{
public Schedule() { Name = ""; StartDate = DateTime.MinValue; LectureList = new List<Lecture>(); }
public string Name { get; set; }
public DateTime StartDate { get; set; }
public List<Lecture> LectureList { get; set; }
}
public class Lecture
{
public string Name { get; set; }
public int Credit { get; set; }
}
What i am trying:
Schedule s = new Schedule();
Type t = Type.GetType("Lecture");
object obj = Activator.CreateInstance(t);
obj.GetType().GetProperty("Name").SetValue(obj, "Math");
obj.GetType().GetProperty("Credit").SetValue(obj, 1);
PropertyInfo pi = s.GetType().GetProperty("LectureList");
Type ti = Type.GetType(pi.PropertyType.AssemblyQualifiedName);
ti.GetMethod("Add").Invoke(pi, new object[] { obj });
It should be something like this:
// gets metadata of List<Lecture>.Add method
var addMethod = pi.PropertyType.GetMethod("Add");
// retrieves current LectureList value to call Add method
var lectureList = pi.GetValue(s);
// calls s.LectureList.Add(obj);
addMethod.Invoke(lectureList, new object[] { obj });
UPD. Here's the fiddle link.
The problem is that you get the Add method of List<Lecture> and try to invoke it with PropertyInfo as the instance invoking the method.
Change:
ti.GetMethod("Add").Invoke(pi, new object[] { obj });
to:
object list = pi.GetValue(s);
ti.GetMethod("Add").Invoke(list, new object[] { obj });
That way pi.GetValue(s) gets the List<Lecture> itself from the PropertyInfo (which only represents the property itself along with its get and set methods, and invoke its Add method with your object[] as arguments.
One more thing. why using:
Type ti = Type.GetType(pi.PropertyType.AssemblyQualifiedName);
When you can just use:
Type ti = pi.PropertyType;
I have 2 types of string: Mer and Spl
// Example
string testMer = "321|READY|MER";
string testSpl = "321|READY|SPL";
Then I will split them:
var splitMer = testMer.Split('|');
var splitSpl = testSpl.Split('|');
I have an object to save them
public class TestObject
{
public int id { get; set; }
public string status { get; set; }
public string type { get; set; }
}
Question: How to convert the Array into the TestObject?
var converted = new TestObject
{
id = int.Parse(splitMer[0]),
status = splitMer[1],
type = splitMer[2]
};
You will need to add some error checking.
var values = new List<string> { "321|READY|MER", "321|READY|SPL" };
var result = values.Select(x =>
{
var parts = x.Split(new [] {'|' },StringSplitOptions.RemoveEmptyEntries);
return new TestObject
{
id = Convert.ToInt32(parts[0]),
status = parts[1],
type = parts[2]
};
}).ToArray();
You just need to use object initializers and set your properties.By the way instead of storing each value into seperate variables, use a List.Then you can get your result with LINQ easily.
var splitMer = testMer.Split('|');
var testObj = new TestObject();
testObj.Id = Int32.Parse(splitMer[0]);
testObj.Status = splitMer[1];
testObj.type = splitMer[2];
How about adding a Constructor to your Class that takes a String as a Parameter. Something like this.
public class TestObject
{
public int id { get; set; }
public string status { get; set; }
public string type { get; set; }
public TestObject(string value)
{
var valueSplit = value.Split('|');
id = int.Parse(valueSplit[0]);
status = valueSplit[1];
type = valueSplit[2];
}
}
Usage:
TestObject tst1 = new TestObject(testMer);
TestObject tst2 = new TestObject(testSpl);
This must be trivial but I can't seem to get it done. Given the following data contract class:
public class SampleItem
{
public int Id { get; set; }
public string StringValue { get; set; }
}
when deserialized to JSON by my WCF service, provides the following output:
[{"Id":1,"StringValue":"Hello"}]
Is there any way to include the class name too? i.e.:
"SampleItem": [{"Id":1,"StringValue":"Hello"}]
You could try something like this:
private dynamic AddClassName(SampleItem item)
{
return new {SampleItem = item};
}
And
var item = new SampleItem {Id = 1, StringValue = "Hello"};
dynamic itemClassName = AppendClassName(item);
string json = new JavaScriptSerializer().Serialize(itemClassName);
Debug.WriteLine(json);
Edit - this works for all types:
private static string GetJsonWrapper<T>(T item)
{
string typeName = typeof(T).Name;
string jsonOriginal = new JavaScriptSerializer().Serialize(item);
return string.Format("{{\"{0}\":{1}}}", typeName, jsonOriginal);
}