关于Xml序列化中使用抽象类导致无法正确序列化而且修改方法过于讨厌这件事
在Xml序列化的时候,存在抽象类属性,由于无法确定具体的类型,导致使用XmlSerializer
的时候报错
参考网上的修改方法
- 使用
XmlInclude
标签
然而在使用的过程中,发现对多个类型标注时,也会报错
abstract class BaseData{}
class DataType1 : BaseData
{
public string prop1 {get;set;}
public string prop2 {get;set;}
}
class DataType2 : BaseData
{
public string prop3 {get;set;}
public string prop4 {get;set;}
}
class XmlData
{
[XmlInclude(typeof(DataType1))]
[XmlInclude(typeof(DataType2))]
...
...
public BaseData Data {get;set;}
}
在我的项目中,几十个接口,每个接口一个Model,那不得难看死。
而且或许是因为使用方法不对,导致报错,暂时未找到解决办法。
生气,还是自己撸一个序列化吧
基本思路就是 反射+递归 创建 MakeSerializer<T>
,在初始化中,使用Expression构建委托
调用方法
MakeSerializer<T>.Invoke(obj)
当然,目前也仅仅是可用的水平,主要还是巩固一下Expression动态构建相关方面的知识,而且目前在项目中使用暂时没出什么问题。
public static class MakeSerializer<T>
{
private static Func<T, string> func;
public static string Invoke(T source)
{
return func.Invoke(source);
}
private static MethodInfo StringBuilderAppend = typeof(StringBuilder).GetMethod("Append", new Type[] { typeof(string) });
private static MethodInfo StringBuilderToString = typeof(StringBuilder).GetMethod("ToString", new Type[0]);
private static MethodInfo GetTypeMethod = typeof(object).GetMethod("GetType", new Type[0]);
private static MethodInfo MakeGenericType = typeof(Type).GetMethod("MakeGenericType", new Type[] { typeof(Type[]) });
private static MethodInfo GetMethod = typeof(Type).GetMethod("GetMethod", new Type[] { typeof(string) });
private static MethodInfo InvokeMethod = typeof(MethodBase).GetMethod("Invoke", new Type[] { typeof(object), typeof(object[]) });
static MakeSerializer()
{
var type = typeof(T);
List<Expression> body = new List<Expression>();
ParameterExpression sourceParameterExpression = Expression.Parameter(type, "source");
// var ret = null;
ParameterExpression varStringBuilder = Expression.Variable(typeof(StringBuilder), "ret");
// var ret = new StringBuilder();
BinaryExpression assignStringBuilder = Expression.Assign(varStringBuilder, Expression.New(typeof(StringBuilder)));
body.Add(assignStringBuilder);
// ret.Append("<?xml version="1.0" encoding="utf-8"?>");
//MethodCallExpression appendExpression = Expression.Call(varStringBuilder, StringBuilderAppend, Expression.Constant("<?xml version=\"1.0\" encoding=\"utf-8\"?>"));
//body.Add(appendExpression);
var rootName = GetTypeRootName(type);
// ret.Append("<root>")
var rootStart = Expression.Call(varStringBuilder, StringBuilderAppend, Expression.Constant(StartFormat(rootName)));
body.Add(rootStart);
ScanProp(type, sourceParameterExpression, varStringBuilder, body);
// ret.Append("</root>")
var rootEnd = Expression.Call(varStringBuilder, StringBuilderAppend, Expression.Constant(EndFormat(rootName)));
body.Add(rootEnd);
MethodCallExpression toStringExpression = Expression.Call(varStringBuilder, StringBuilderToString);
body.Add(toStringExpression);
var block = Expression.Block(new ParameterExpression[] { varStringBuilder }, body);
var lambdaExpression = Expression.Lambda(block, sourceParameterExpression);
func = (Func<T, string>)lambdaExpression.Compile();
}
private static string GetTypeRootName(Type type)
{
var attrs = type.GetCustomAttributes(typeof(XmlRootAttribute), true);
if (attrs.Length == 0)
return type.Name;
var root = (XmlRootAttribute)attrs[0];
return root.ElementName;
}
private static void ScanProp(Type type, ParameterExpression parameter, ParameterExpression var, List<Expression> body)
{
var props = type.GetProperties();
foreach (var prop in props)
{
if (!prop.CanWrite) continue;
if (prop.PropertyType.IsAssignableFrom(typeof(XmlData)) || typeof(XmlData).IsAssignableFrom(prop.PropertyType))
{
//ScanProp(prop.PropertyType, parameter, var, body);
// var type = source.XmlData.GetType()
var propExp = Expression.Property(parameter, prop);
var getTypeExp = Expression.Call(propExp, GetTypeMethod);
var gType = Expression.Constant(typeof(MakeSerializer<>));
var getTypeMethodExp = Expression.Call(gType, MakeGenericType, Expression.NewArrayInit(typeof(Type), getTypeExp));
// var invoke = gType.GetMethod("Invoke");
var getInvokeExp = Expression.Call(getTypeMethodExp, GetMethod, Expression.Constant("Invoke"));
// var str = invoke.Invoke(source.XmlData)
var resultExp = Expression.Call(getInvokeExp, InvokeMethod, Expression.Constant(null), Expression.NewArrayInit(typeof(object), propExp));
MethodCallExpression methodCallExpression1 = Expression.Call(var, StringBuilderAppend, Expression.Convert(resultExp, typeof(string)));
body.Add(methodCallExpression1);
}
else
{
var propStart = Expression.Call(var, StringBuilderAppend, Expression.Constant(StartFormat(GetMemberName(prop))));
body.Add(propStart);
MemberExpression memberExpression = Expression.Property(parameter, prop);
var propValue = Expression.Call(var, StringBuilderAppend, memberExpression);
body.Add(propValue);
var propEnd = Expression.Call(var, StringBuilderAppend, Expression.Constant(EndFormat(GetMemberName(prop))));
body.Add(propEnd);
}
}
}
private static string StartFormat(string s)
{
return $"<{s}>";
}
private static string EndFormat(string s)
{
return $"</{s}>";
}
private static string GetMemberName(MemberInfo Member)
{
if (Member.GetCustomAttributes(typeof(XmlElementAttribute), true).Count() > 0)
{
return ((XmlElementAttribute)Member.GetCustomAttributes(typeof(XmlElementAttribute), true)[0]).ElementName;
}
else
{
return Member.Name;
}
}
}