Search code examples
c#genericsjson.nettype-conversionsystem.reflection

Converting a value with object type, declared as List<T> to List<U> using Dictionary<Type, Type> with the types corresponding to T and U respectively


I am refactoring on a legacy code where back compatiblity is critical. I got stuck in trying to develop a converter for more than 40 different types to be serialized.

I need to convert List<T>s to List<U>s where the U = TypeDictionary<Type,Type>[T].

I did a lot of search, and worked on the issue for two days. Still I couldn't reached to a sloution, maybe due to not knowing right keywords and I feel got lost. I would appreciate any advice.

EDİT: The problem was different from what I initially assumed. The problem was converting a variable which has actual type Object and declared type List<> into actual List<> type. Excuse me for misleading.

This is the static class I use to copy the properties of a class:

public static class PropertyCopier
    {
        public static U CopyFrom<T, U>(T source, U target)
        {
            var targetObj = Activator.CreateInstance<U>();
            foreach (var targetProp in targetObj.GetType().GetProperties())
            {
                var sourceProp = source.GetType().GetProperty(targetProp.Name);
                if (sourceProp == null)
                {
                    var sourceField = source.GetType().GetField(targetProp.Name);
                    if (sourceField == null)
                    {
                        throw new Exception("there is no field or property with same name");
                    }
                    targetProp.SetValue(targetObj, sourceField.GetValue(source));
                }
                else
                {
                    targetProp.SetValue(targetObj, sourceProp.GetValue(source));
                }
            }
            return targetObj;
        }
    }

I defined a class using Json.Net; "CustomTypeConverter : JsonConverter". These three private methods and the dictionary are the members of the CustomTypeConverter:

Private Dictionary<Type,Type> TypeToProxy = new Dictionary<Type,Type>();

private bool isGenericList(Type type)
    {
        return (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>)));
    }

private Type GetFirstArgumentType(Type type)
    {
        return type.GetGenericArguments()[0];
    }

private List<U> ConvertListToProxy<T, U>(List<T> source, U target)
    {
        var lst = source.Select(x => PropertyCopier.CopyFrom(x, target)).ToList();
        return lst;
    }

and this is a section from the WriteJson method of CustomTypeConverter

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    //..
         if ( isGenericList(value.GetType()) )
            {
                Type listType = typeof(List<>);
                listType.MakeGenericType(GetFirstArgumentType(value.GetType()));
                var newVal = Convert.ChangeType(value, listType);
                var newList = ConvertListToProxy(newVal, TypeToProxy[value.GetType()]);
                serializer.Serialize(writer, newList);
            }
    //..
    }

This is the screenshot showing the error message enter image description here


Solution

  • I think, I solved the problem thanks to the suggestions and help I took from you.

    Edit: I used dynamic type to skip type checking and types are taken by using Reflection methods. The type transformation is handled via a wrapping method PropertyCopier.ProxyFrom.

    public List<dynamic> ConvertListToProxy(dynamic source)
        {
            // source is List<>
            Type T = source.GetType().GenericTypeArguments[0];
            Type U = _typeToProxy[T];
            List<dynamic> target = new List<dynamic>();
            try
            {
                foreach (dynamic item in source)
                {
                    target.Add(PropertyCopier.ProxyFrom(item, U));
                }
            }
            catch (Exception ex)
            {                
                Logger.Get().WriteLine("***-Exception-***");
                Logger.Get().WriteLine($"***-Trying to convert to List<{U.Name}>");
                Logger.Get().WriteLine(ex.Message);
                Logger.Get().WriteLine($"-***");
    
            }
            return target;
        }
    

    And the calling section of the WriteJson method is modifed like this:

     if (isGenericList(value.GetType()))
                {
                    var newList = ConvertListToProxy(value);
                    serializer.Serialize(writer, newList);
                }
                else
                {
                    if (_types.Contains(type))
                    {                    
                    var newObj = PropertyCopier.CopyFrom(Convert.ChangeType(value, type), _typeToProxy[type]);
                    serializer.Serialize(writer, newObj);
                    }
                }