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