0

EDIT: All things working now, I was getting the Type.GetType() wrong.

The correct answer is proven correct with 1 change:

.GetMethod("Deserialize", new[] { typeof(string) })

I want to create different types based on some text. I have tried the following without success:

JSON input:

[{"Type":"Book","Details":{"Name":"Book1","Chapter":"1","StartPage":"5","EndPage":"23"}},{"Type":"WebPage","Details":{"Name":"Page1","Url":"sometesturl.com","PageTypeIDs":"1"}}]

Since I do not want to change code every time a new type is added I tought reflection might be the solution.

List<Source> sources = new List<Source>();

dynamic[] items = jsSerializer.Deserialize<dynamic>(validjson);
foreach (var item in items)
        {
            string type = item["Type"];

            string serialized = jsSerializer.Serialize(item["Details"]);

            Type t = Type.GetType(type);
            var instance = createInstanceFromJSON(t, serialized);

            sources.Add(instance);
        }

Will give the following error: 'Cannot be converted from 'System.Type' to 'ProjectManager.Sources'' note that the type is in a seperate DLL called ProjectManager.

Here's the method:

private T createInstanceFromJSON<T>(T type, string json) where T : class
    {
        ConstructorInfo construtorInfo = typeof(T).GetConstructor(new[] { typeof(string) });
        ParameterInfo contructor = construtorInfo.GetParameters()[0];
        object defaultValue = contructor.DefaultValue;

        var item = (T)Activator.CreateInstance(typeof(T), defaultValue);

        item = jsSerializer.Deserialize<T>(json);

        return item;

    }
Noel Heesen
  • 223
  • 1
  • 4
  • 14
  • You're passing an instance of System.Type into createInstanceFromJSON. Within createInstanceFromJSON, T is now System.Type. Then you're calling `typeof(T)` on this object, which returns... System.Type. This isn't what you want. `T type` should be `Type type`, and you should pass `typeof(ProjectManager.Sources)` into createInstanceFromJSON. Then, don't do `typeof(T).GetConstructor`, just do `type.GetConstructor`. Your code is a bit of a mess, and you should debug it step by step to see what's going on. –  Jun 02 '16 at 13:36
  • In addition to that, you could remove the reflection completely since you return the result of the `jsSerializer.Deserializer` call (in your `createInstanceFromJSON`) which doesn't depend on the things you do with reflection. – Dirk Jun 02 '16 at 13:40
  • You cannot pass a Type as a generic and expect to return something other than a type. Check out this for more information about using reflection with generics http://stackoverflow.com/questions/266115/pass-an-instantiated-system-type-as-a-type-parameter-for-a-generic-class – Simon Jun 02 '16 at 13:40

2 Answers2

1

I think what you wanted to do is

// no need to make this method generic
private Source createInstanceFromJSON(Type type, string json)
{
    // use reflection to get the method for type "type"
    var deserializeMethod =
        jsSerializer.GetType()
                    .GetMethod("Deserialize")
                    .MakeGenericMethod(new[] { type });

    // invoke the method on the jsSerializer object
    var item = (Source)deserializeMethod.Invoke(jsSerializer, new[] { json });

    return item;
}

Then you can use

string type = item["Type"];
string serialized = jsSerializer.Serialize(item["Details"]);

Type t = Type.GetType(type);
Source instance = createInstanceFromJSON(t, serialized);

sources.Add(instance);
Dirk
  • 10,668
  • 2
  • 35
  • 49
0

Your generic parameter T will always have the type System.Type as it is the type describing your Object that you pass as the method parameter.

You have to construct your constructor method differently as you have to call that one already by a reflective call if you want to use the Generic <T> call. Otherwise the compiler will have no idea of what to invoke since T will only be determined T at runtime.

OR

You can salvage your current code using reflection to call that method with the correct type. Remove the first parameter of the method...

private T createInstanceFromJSON<T>(string json) where T : class
{
    ConstructorInfo construtorInfo = typeof(T).GetConstructor(new[] { typeof(string) });
    ParameterInfo contructor = construtorInfo.GetParameters()[0];
    object defaultValue = contructor.DefaultValue;

    var item = (T)Activator.CreateInstance(typeof(T), defaultValue);

    item = jsSerializer.Deserialize<T>(json);

    return item;

}

and then use MethodInfo to call the method with your generic type:

Type t= Type.GetType(type);
MethodInfo method = this.GetType().GetMethod("createInstanceFromJSON", BindingFlags.NonPublic);
method = method.MakeGenericMethod(t);
var instance = method.Invoke(this, serialized);

Although I do not know if that is really helpful to you, it should at least work.

Adwaenyth
  • 2,020
  • 12
  • 24