Reputation: 6650
I have a variable inputs
defined as List<ExpandoObject>
This was a result of de-serialization of jsonList, which is a JSON array of objects of different structures:
dynamic inputs = JsonConvert.DeserializeObject<List<ExpandoObject>>(jsonList, converter);
Looping through them I can get a target type of each object, because they all contain a property Type
that has a ClassName
of a target object.
foreach (dynamic input in inputs)
{
// Inside that loop I can get the type
var inputType = Type.GetType(string.Format("WCFService.{0}", input.Type));
// WCFService is a namespace
// How can I convert here dynamic **input** object
// into an object of type inputType ??
}
Basically I want inside that loop convert input object into corresponding type specified as string in input.Type
Any help is appreciated.
EDIT
Inside that for-each loop I want to do something like this:
var json = JsonConvert.SerializeObject(input);
Type T = Type.GetType(string.Format("WCFService.{0}", input.Type));
T obj = JsonConvert.DeserializeObject<typeof(T)>(json); // this line fails compilation
This way obj will be a strongly-typed object. I use json serialization in order to do deserialization back, this way all json properies will be replicated into strongly-typed obj automatically. But the above code doesnt compile, complaining on T for the last line:
The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)
EDIT2
Just FYI, the incoming jsonList has this structure, each object in that array can have different properties, except of Name and Type:
[
{
"Name": "PLAN-A",
"Type": "CalcInputTypes1",
"CS": 1.1111,
"CUSTOM_DATE1": "2015-05-22",
"CUSTOM_EARN1": 65500.0,
"GENDER": "Male"
},
{
"Name": "PLAN-B",
"Type": "CalcInputTypes2",
"CS": 2.22222,
"CUSTOM_DATE2": "2015-05-23",
"CUSTOM_EARN2": 12000.0,
"PROVINCE": "Ontario"
}
]
CalcInputTypes1, CalcInputTypes2 and most likely CalcInputTypes3,4,5... are the types of such objects in that array...
SOLUTION
Thanks guys for your help, especially for suggesting using JObject instead of ExpandoObject, that made the solution much easier and simpler: Note: "thing" will never work, since T in this case must be known at compile time, but I neet to determine type at runtime, so the solution will be like that:
public CalcOutputTypes Calculate2(string jsonList)
{
var jobjects = JsonConvert.DeserializeObject<List<JObject>>(jsonList);
foreach (var jobject in jobjects)
{
Type runtimeType = Type.GetType(string.Format("WCFService.{0}", jobject.GetValue("TYPE")));
var input = jobject.ToObject(runtimeType); // Here we convert JObject to the defined type that just created runtime
// At this moment you have a strongly typed object "input" (CalcInputTypes1 or CalcInputTypes2 or...)
}
return new CalcOutputTypes() { STATUS = "Everything is OK !! (input was: json array of heterogeneous objects)" }; // HERE YOU RETURN CalcOutputTypes OBJECT
}
Upvotes: 4
Views: 4638
Reputation: 628
You do not need list of ExpandoObjext's, just use CustomCreationConverter like described in Deserializing heterogenous JSON array into covariant List<> using JSON.NET so all credits to @JimSan
public class Example
{
[Test]
public void Test()
{
var json =
"[\r\n {\r\n \"Name\": \"PLAN-A\",\r\n \"Type\": \"CalcInputTypes1\",\r\n \"CS\": 1.1111,\r\n \"CUSTOM_DATE1\": \"2015-05-22\",\r\n \"CUSTOM_EARN1\": 65500.0,\r\n \"GENDER\": \"Male\"\r\n },\r\n {\r\n \"Name\": \"PLAN-B\",\r\n \"Type\": \"CalcInputTypes2\",\r\n \"CS\": 2.22222,\r\n \"CUSTOM_DATE2\": \"2015-05-23\",\r\n \"CUSTOM_EARN2\": 12000.0,\r\n \"PROVINCE\": \"Ontario\"\r\n }\r\n]";
var result = JsonConvert.DeserializeObject<List<Item>>(json, new JsonItemConverter());
Assert.That(result[0], Is.TypeOf<CalcInputTypes1>());
Assert.That(((CalcInputTypes1)result[0]).Gender, Is.EqualTo("Male"));
Assert.That(result[1], Is.TypeOf<CalcInputTypes2>());
Assert.That(((CalcInputTypes2)result[1]).Province, Is.EqualTo("Ontario"));
}
public class JsonItemConverter : Newtonsoft.Json.Converters.CustomCreationConverter<Item>
{
public override Item Create(Type objectType)
{
throw new NotImplementedException();
}
public Item Create(Type objectType, JObject jObject)
{
var type = (string)jObject.Property("Type");
switch (type)
{
case "CalcInputTypes1":
return new CalcInputTypes1();
case "CalcInputTypes2":
return new CalcInputTypes2();
}
throw new ApplicationException(String.Format("The given type {0} is not supported!", type));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load JObject from stream
var jObject = JObject.Load(reader);
// Create target object based on JObject
var target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
}
public abstract class Item
{
public string Type { get; set; }
}
public class CalcInputTypes1 : Item
{
[JsonProperty("GENDER")]
public string Gender { get; set; }
}
public class CalcInputTypes2 : Item
{
[JsonProperty("PROVINCE")]
public string Province { get; set; }
}
}
Upvotes: 1
Reputation: 117046
You could avoid using ExpandoObject
and instead use LINQ to JSON directly, like so:
var query = from obj in JsonConvert.DeserializeObject<List<JObject>>(jsonList, converter)
let jType = obj["Type"]
where jType != null
let type = Type.GetType(string.Format("WCFService.{0}", (string)jType))
where type != null
where obj.Remove("Type") // Assuming this is a synthetic property added during serialization that you want to remove.
select obj.ToObject(type);
var objs = query.ToList();
If you need to pass that converter
down to each specific ToObject()
call, you can do:
var settings = new JsonSerializerSettings();
settings.Converters.Add(converter);
var serializer = JsonSerializer.Create(settings);
var query = from obj in JsonConvert.DeserializeObject<List<JObject>>(jsonList, settings)
let jType = obj["Type"]
where jType != null
let type = Type.GetType(string.Format("WCFService.{0}", (string)jType))
where type != null
where obj.Remove("Type") // Assuming this is a synthetic property added during serialization that you want to remove.
select obj.ToObject(type, serializer);
var objs = query.ToList();
Upvotes: 2
Reputation: 7830
Another possible solution would be as @dbc suggested in a comment to use the Newtonsoft.Json method JsonConvert.DeserializeObject(json, type)
like this:
private T convertTo<T>(string json)
{
return (T)JsonConvert.DeserializeObject(json, typeof(T));
}
var json = // some serialized json ...
var o = convertTo<MyCustomType>(json);
where MyCustomType
is the type from your input (var inputType = Type.GetType(string.Format("WCFService.{0}", input.Type));
.
Don't forget the implicit cast (T)
!
Another possibility is to write your own type converter using the .NET framework build-in TypeConverter class.
Upvotes: 1