Я публикую это на пятидесятилетней основе.
50% вероятности, что я упускаю что-то очевидное, и другие скажут мне, почему я глупец - в таком случае,это можно сделать проще?Скажите мне, что я неправ, если вы готовы, но, пожалуйста, не кусайте слишком сильно!
50% мое решение действительно полезно, и другие могут оценить его.
ЛибоКстати, я надеюсь кое-что узнать.
Я использую сериализацию XML для очень тяжелого проекта, где у меня есть производственная линия аналитики.В производственной линии много этапов, поэтому я написал несколько очень абстрактных базовых механизмов, которые легли бы в их основу.Было много работы!60 тысяч строк кода.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();
Это позволяет отображать локально-ограниченный тип в интерфейсе.
Это вспомогательные функции в основном методе:
public PropertyInfo[] GetPropertyInfos(object theObject)
{
// /5643014/kak-perechislit-vse-peremennye-klassa
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)
{
// /5643014/kak-perechislit-vse-peremennye-klassa
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;
}
Итак
- Является ли этот подход правильным?
- Можно ли его улучшить или сократить?
- Кто-то уже опубликовал лучшее решение?