c# - 处理集合和接口的 XML 序列化的通用解决方案
问题描述
我在五十个基础上发布这个。
50% 的可能性我遗漏了一些明显的东西,其他人会告诉我为什么我是个傻瓜——在这种情况下,这可以更简单地完成吗?如果你愿意,告诉我我错了——但请不要咬得太紧!
50% 我的解决方案实际上是有用的,其他人可能会喜欢它。
不管怎样,我希望能学到一些东西。
我正在将 XML 序列化用于一个非常繁重的项目,其中我有一条分析生产线。生产线有很多步骤,所以我写了一些非常抽象的基础机械来作为它们的基础。已经做了很多工作!60k 行代码。1,000 种类型。
我现在要做的是将生产线的任何部分序列化为 XML,而不必为生产线中的每个组件编写自定义序列化代码。我想要一个单一的方法,在一个基类中。到目前为止,这没有什么不寻常的。
但告密者是 C#(嗯,.NET)
- 无法序列化字典
- 无法序列化接口
- 在 XML 序列化中提供有限的调试、日志记录和可检查性。
所以我在我的基类中编写了以下函数,一切都继承。
public void WriteXml(XmlWriter writer)
{
Console.WriteLine("############### WRITING XML FOR " + GetType() + " " + this.Name);
foreach (FieldInfo fieldInfo in this.GetFieldInfos(this))
{
try
{
string fieldName = fieldInfo.Name;
var fieldValue = fieldInfo.GetValue(this);
if (!IsBackFieldName(fieldName))
{
Console.WriteLine("Serializing\t" + fieldInfo.FieldType + "\t\t" + fieldName + "\t" + fieldValue);
if (fieldInfo.FieldType.IsDictionary())
{
// TODO intercept any Dictionary type and convert to SerializableDictionary
;
}
writer.WriteStartElement(fieldName);
if (fieldInfo.FieldType.IsXmlSerializable())
{
XmlSerializer xmlSerializer;
if (fieldInfo.FieldType.IsInterface)
{
// look through the interface to the underlying type, which will have a parameterless constructor for serialization
IData castedValue = fieldValue as IData;
Type lookThroughClass = castedValue.SerializationType;
xmlSerializer = new XmlSerializer(lookThroughClass);
}
else
xmlSerializer = new XmlSerializer(fieldInfo.FieldType);
// serialization here can be built-in or overriden if IXmlSerializable is implemented
xmlSerializer.Serialize(writer, fieldValue);
}
else
{
writer.WriteComment("Not serializable " + fieldInfo.FieldType);
}
writer.WriteEndElement();
}
else
{
// skip backing field
Console.WriteLine("SKIPPING\t" + fieldInfo.FieldType + "\t\t" + fieldName + "\t" + fieldValue);
;
}
}
catch (Exception e)
{
Console.WriteLine("Error writing XML: " + e.Message);
}
}
foreach (PropertyInfo propertyInfo in GetPropertyInfos(this))
{
try
{
string propertyName = propertyInfo.Name;
var propertyValue = propertyInfo.GetValue(this);
if (!IsBackFieldName(propertyName))
{
Console.WriteLine("Serializing\t" + propertyInfo.PropertyType + "\t\t" + propertyName + "\t" + propertyValue);
// TODO intercept any Dictionary type and convert to SerializableDictionary
if (propertyInfo.PropertyType.IsDictionary())
{
// TODO intercept any Dictionary type and convert to SerializableDictionary
;
}
writer.WriteStartElement(propertyName);
if (propertyInfo.PropertyType.IsXmlSerializable())
{
XmlSerializer xmlSerializer;
if (propertyInfo.PropertyType.IsInterface)
{
// look through the interface to the underlying type, which will have a parameterless constructor for serialization
IData castedValue = propertyValue as IData;
Type lookThroughClass = castedValue.SerializationType;
xmlSerializer = new XmlSerializer(lookThroughClass);
}
else
xmlSerializer = new XmlSerializer(propertyInfo.PropertyType);
// serialization here can be built-in or overriden if IXmlSerializable is implemented
xmlSerializer.Serialize(writer, propertyValue);
}
else
{
writer.WriteComment("Not serializable " + propertyInfo.PropertyType);
}
writer.WriteEndElement();
}
else
{
// skip backing field
Console.WriteLine("SKIPPING\t" + propertyInfo.PropertyType + "\t\t" + propertyName + "\t" + propertyValue);
}
}
catch (Exception e)
{
Console.WriteLine("Error writing XML: " + e.Message);
}
}
return;
}
支持的基础设施是:
public interface IData
: IXmlSerializable
{
#region Members
string Name { get; }
#endregion
#region Methods
/// <summary>
/// Allows us to pass the underlying type back out through an interface.
/// We have to know the actual type for serialization, because interfaces cannot be instantiated.
/// </summary>
Type SerializationType { get; }
#endregion
}
IData 由生产线的所有成员实现,它们也继承了基类中的 XML 写入和读取方法。关键(可能是偷偷摸摸的)部分是每个子类都必须SerializationType
在本地实现:
public Type SerializationType => GetType();
这允许本地范围的类型在界面中可见。
这些是 main 方法中的辅助函数:
public PropertyInfo[] GetPropertyInfos(object theObject)
{
// https://stackoverflow.com/questions/6536163/how-to-list-all-variables-of-class
BindingFlags bindingFlags = BindingFlags.Instance |
BindingFlags.NonPublic |
BindingFlags.Public;
// Type theType = this.GetType();
Type theType = theObject.GetType();
PropertyInfo[] propertyInfos = theType.GetProperties(bindingFlags);
return propertyInfos;
}
public FieldInfo[] GetFieldInfos(object theObject)
{
// https://stackoverflow.com/questions/6536163/how-to-list-all-variables-of-class
BindingFlags bindingFlags = BindingFlags.Instance |
BindingFlags.NonPublic |
BindingFlags.Public;
// Type theType = this.GetType();
Type theType = theObject.GetType();
FieldInfo[] fieldInfos = theType.GetFields(bindingFlags);
return fieldInfos;
}
在静态扩展类中:
public static bool IsXmlSerializable(this Type testType)
{
if (testType.IsSerializable)
return true;
// just for good measure
var interfaces = testType.GetInterfaces().ToList();
if (interfaces.Contains(typeof(IXmlSerializable)))
return true;
return false;
}
所以
- 这种方法合理吗?
- 可以改进或缩短吗?
- 其他人是否已经发布了更好的解决方案?
解决方案
推荐阅读
- reactjs - Gatsby 内容安全策略
- java - Java 使用缩短的路径创建临时文件夹,并在尝试访问放置在其中的文件时抛出“未找到”异常
- raster - 将数据框导出为直到格式
- matlab - 如何在定向后改变椭球的尺寸
- java - 引导完成后前台服务需要 40 到 50 秒的时间吗?
- python - 如何检查 GCP 存储中的文件是否公开?
- ios15 - iOS 15 私有中继例外列表
- string - 有没有办法恢复损坏的 Unicode 字符串?
- json - 为什么需要在 Terraform 上的此参数定义中使用双斜杠?
- django - 从 SQLite DB 获取特定数据到 django 表单