How can I convert each object in List <ExpandoObject> to its own type?

I have a variable defined as inputs

List<ExpandoObject>

This was the result of de-serializing 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 the target type of each object because they all contain the property that the target has . Type

ClassName

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 to, inside this loop, convert the input object to the appropriate type specified as a string in input.Type

Any help is appreciated.

EDIT

Inside 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

      

So obj will be a strongly typed object. I am using json serialization to do the deserialization so all json properties will be automatically replicated to a strongly typed obj. But the above code doesn't compile, complaining about T for the last line:

Can't find the type or namespace name 'T' (are you missing using a directive or assembly reference?)

EDIT2

Just FYI, the incoming jsonList has this structure, each object in this array can have different properties except 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 this array ...

Decision

Thanks guys for your help, especially for suggesting to use JObject instead of ExpandoObject, which made the solution much simpler and easier: Note: the "thing" will never work, since the T in this case should be known at compile time, but I have to define type at runtime, so the solution would be like this:

    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 
    }

      

+3


source to share


3 answers


You can avoid using ExpandoObject

and use LINQ to JSON instead , like:

        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 this converter

before each specific call ToObject()

, 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();

      

+1


source


Another possible solution would be like @dbc suggested in the 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 of your input ( var inputType = Type.GetType(string.Format("WCFService.{0}", input.Type));

.



Don't forget the implicit listing (T)

!

Another possibility is to write your own type converter using the built-in .NET framework TypeConverter class .

+1


source


You don't need an ExpandoObjext list, just use a CustomCreationConverter as described in deserializing a heterogeneous JSON array to a covariant list <> using JSON.NET , so all credits @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; }
    }
}

      

0


source







All Articles