从Dictionary <string,object> </string,object>初始化类型为T的对象

时间:2010-03-01 02:10:11

标签: c# json reflection serialization

我正在寻找更通用/“标准”的方法来从一对字符串对象中实例化某种类型T的对象。对我来说,看起来应该有一些众所周知的方法,但我找不到它,所以我想出了这段代码。 有人知道更好的事吗?

// usage
public class test
    {
    public int field1;
    public string field2;
    public bool field3;
    public string[] field4;
    public IDictionary<string,object> field5 { get; set; }

    public static IDictionary<string,object> dynamic()
        {
        return new Dictionary<string,object>{
            { "field1", 2 },
            { "field2", "string" },
            { "field3", true },
            { "field4", new[] { "id3", "id4", "id5" } },
            { "field5", new Dictionary<string,object>{ { "id1", "" } } }
            };
        }
    }

...
var r = new dynamic_data_serializer<test>().create( test.dynamic() );
...

// 
public class dynamic_data_serializer< T >
    {
    public T create( object obj )
        {
        var result = default(T);
        if ( obj == null )
            return result;

        var ttype = typeof(T);
        var objtype = obj.GetType();
        if ( ttype.IsAssignableFrom( objtype ) ) {
            result = (T)obj;
            return result;
            }

        if ( ttype.IsClass ) { // custom classes, array, dictionary, etc.
            result = Activator.CreateInstance<T>();

            if ( objtype == typeof(IDictionary<string,object>) || 
                    objtype == typeof(Dictionary<string,object>) ) {
                var obj_as_dict = obj as IDictionary<string,object>; 
                var fields = ttype.GetFields();
                if ( fields.Length > 0 )
                    set_fields_from( result, fields, obj_as_dict );

                var properties = ttype.GetProperties();
                if ( properties.Length > 0 )
                    set_properties_from( result, properties, obj_as_dict );
                }
            }    
        return result;
        }

    private void set_fields_from( T _this_, FieldInfo[] fields, IDictionary<string,object> obj ) {
        foreach ( var fld in fields ) {
            var v = find( obj, fld.Name );
            if ( v != null ) {
                var mobj = call_deserialize( fld.FieldType, v );
                fld.SetValue( _this_, mobj );
                }
            }
        }

    private void set_properties_from( T _this_, PropertyInfo[] properties, IDictionary<string,object> obj ) {
        foreach ( var prop in properties ) {
            var v = find( obj, prop.Name );
            if ( v != null ) {
                var mobj = call_deserialize( prop.PropertyType, v );
                prop.SetValue( _this_, mobj, null );
                }
            }
        }

    private object find( IDictionary<string,object> obj, string name ) {
        foreach ( var kv in obj )
            if ( string.Compare( kv.Key, name, true ) == 0 )
                return kv.Value;
        return null;
        }

    private object call_deserialize( Type des_type, object value ) {
        var gtype = typeof(dynamic_data_serializer<>);
        Type desz_type = gtype.MakeGenericType( new[]{ des_type } );
        object desz = Activator.CreateInstance( desz_type );
        var method_type = desz_type.GetMethod( "create" ); 
        return method_type.Invoke( desz, new[]{ value } );
        }
    }
}

// usage public class test { public int field1; public string field2; public bool field3; public string[] field4; public IDictionary<string,object> field5 { get; set; } public static IDictionary<string,object> dynamic() { return new Dictionary<string,object>{ { "field1", 2 }, { "field2", "string" }, { "field3", true }, { "field4", new[] { "id3", "id4", "id5" } }, { "field5", new Dictionary<string,object>{ { "id1", "" } } } }; } } ... var r = new dynamic_data_serializer<test>().create( test.dynamic() ); ... // public class dynamic_data_serializer< T > { public T create( object obj ) { var result = default(T); if ( obj == null ) return result; var ttype = typeof(T); var objtype = obj.GetType(); if ( ttype.IsAssignableFrom( objtype ) ) { result = (T)obj; return result; } if ( ttype.IsClass ) { // custom classes, array, dictionary, etc. result = Activator.CreateInstance<T>(); if ( objtype == typeof(IDictionary<string,object>) || objtype == typeof(Dictionary<string,object>) ) { var obj_as_dict = obj as IDictionary<string,object>; var fields = ttype.GetFields(); if ( fields.Length > 0 ) set_fields_from( result, fields, obj_as_dict ); var properties = ttype.GetProperties(); if ( properties.Length > 0 ) set_properties_from( result, properties, obj_as_dict ); } } return result; } private void set_fields_from( T _this_, FieldInfo[] fields, IDictionary<string,object> obj ) { foreach ( var fld in fields ) { var v = find( obj, fld.Name ); if ( v != null ) { var mobj = call_deserialize( fld.FieldType, v ); fld.SetValue( _this_, mobj ); } } } private void set_properties_from( T _this_, PropertyInfo[] properties, IDictionary<string,object> obj ) { foreach ( var prop in properties ) { var v = find( obj, prop.Name ); if ( v != null ) { var mobj = call_deserialize( prop.PropertyType, v ); prop.SetValue( _this_, mobj, null ); } } } private object find( IDictionary<string,object> obj, string name ) { foreach ( var kv in obj ) if ( string.Compare( kv.Key, name, true ) == 0 ) return kv.Value; return null; } private object call_deserialize( Type des_type, object value ) { var gtype = typeof(dynamic_data_serializer<>); Type desz_type = gtype.MakeGenericType( new[]{ des_type } ); object desz = Activator.CreateInstance( desz_type ); var method_type = desz_type.GetMethod( "create" ); return method_type.Invoke( desz, new[]{ value } ); } } }

3 个答案:

答案 0 :(得分:2)

DataContractJsonSerializer太慢,但你正在使用反射?如果你必须反序列化大量的对象,我建议使用编译的lambdas而不是反射。 lambda只能设置属性,而不能设置字段(至少在.Net 3.5中),所以你可能需要调整你使用它的类,但这是值得的,因为它快1000倍。

这是一个函数,它创建一个属性设置器,给定一个类型,并为要设置的属性设置PropertyInfo

    static Action<object, TValue> MakeSetter<TValue>(Type tclass, PropertyInfo propInfo)
    {
        var t = lambda.Expression.Parameter(typeof(object), "t");
        var v = lambda.Expression.Parameter(typeof(TValue), "v");
        // return (t, v) => ((tclass)t).prop = (tproperty)v
        return (Action<object, TValue>)
            lambda.Expression.Lambda(
                lambda.Expression.Call(
                    lambda.Expression.Convert(t, tclass),
                    propInfo.GetSetMethod(),
                    lambda.Expression.Convert(v, propInfo.PropertyType)),
                t,
                v)
            .Compile();
    }

每个类都有一个setter的字典,每当你必须设置一个类的属性时,你会在字典中查找该属性的setter并使用要赋值的值调用它,如下所示: setters[propName](_this_, value);

答案 1 :(得分:1)

我可能会建议FormatterServices.PopulateObjectMembers,除了a:这仍然很慢AFAIK,而b:我尝试过(下面)并且它似乎想要在属性上抛出异常(不知道为什么;没有看起来太深了。另一个选项可能是Expression,但你并不是每次都想要Compile(最好只做一次并缓存它,但这需要一种已知的格式)。

public T create(object obj)
{  // simplified for illustration
    var bindings = obj as IDictionary<string, object>;
    Type type = typeof(T);
    var func = Expression.Lambda<Func<T>>(Expression.MemberInit(
        Expression.New(type),
        from pair in bindings
        let member = type.GetMember(pair.Key).Single()
        select (MemberBinding)Expression.Bind(member, Expression.Constant(pair.Value))));
    return func.Compile().Invoke();
}

最后,您可以缓存一组预编译的Action<object> setter(根据成员名称键入)。实际上,这可能是你最好的选择。属性很简单(使用Delegate.CreateDelegate) - 字段可能需要DynamicMethod - 但是如果你无法提前预测布局,那么它的开销就会最小。

对于keyed / IL方法(你不会更快):

public class dynamic_data_serializer<T>
{
    public T create(object obj)
    {
        T inst = Activator.CreateInstance<T>();
        var bindings = obj as IDictionary<string, object>;
        foreach (var pair in bindings)
        {
            setters[pair.Key](inst, pair.Value);
        }
        return inst;
    }
    private static readonly Dictionary<string, Action<T, object>> setters;
    static dynamic_data_serializer()
    {
        setters = new Dictionary<string, Action<T, object>>(StringComparer.Ordinal);
        foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
            setters.Add(prop.Name, CreateForMember(prop));
        }
        foreach (FieldInfo field in typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance)) {
            setters.Add(field.Name, CreateForMember(field));
        }
    }
    static Action<T, object> CreateForMember(MemberInfo member)
    {
        bool isField;
        Type type;
        switch (member.MemberType) {
            case MemberTypes.Property:
                isField = false;
                type = ((PropertyInfo)member).PropertyType;
                break;
            case MemberTypes.Field:
                isField = true;
                type = ((FieldInfo)member).FieldType;
                break;
            default:
                throw new NotSupportedException();
        }
        DynamicMethod method = new DynamicMethod("__set_" + member.Name, null, new Type[] { typeof(T), typeof(object) });
        ILGenerator il = method.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        if(type != typeof(object)) {
            il.Emit(type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type);
        }
        if (isField) {il.Emit(OpCodes.Stfld, (FieldInfo)member);}
        else { il.EmitCall(OpCodes.Callvirt, ((PropertyInfo)member).GetSetMethod(), null);  }

        il.Emit(OpCodes.Ret);
        return (Action<T, object>)method.CreateDelegate(typeof(Action<T, object>));
    }
}

答案 2 :(得分:0)

DataContractJsonSerializer

为什么要制作自定义序列化程序而不使用DataContractJsonSerializer?

修改

如果DataContractJsonSerializer不适合您,您可以尝试JSON.Net。有效地实现序列化程序并不是一件容易的事,你可能不想进入很多陷阱和特殊情况。顺便说一下,你的代码示例大量使用反射很慢,我怀疑它的性能会比DataContractJsonSerializer或JSON.Net好。