简体   繁体   中英

C# Reflection, create instance from text

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;

    }

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM