Общее решение для XML-сериализации, которое обрабатывает коллекции и интерфейсы - PullRequest
0 голосов
/ 09 июня 2018

Я публикую это на пятидесятилетней основе.

50% вероятности, что я упускаю что-то очевидное, и другие скажут мне, почему я глупец - в таком случае,это можно сделать проще?Скажите мне, что я неправ, если вы готовы, но, пожалуйста, не кусайте слишком сильно!

50% мое решение действительно полезно, и другие могут оценить его.

ЛибоКстати, я надеюсь кое-что узнать.

Я использую сериализацию XML для очень тяжелого проекта, где у меня есть производственная линия аналитики.В производственной линии много этапов, поэтому я написал несколько очень абстрактных базовых механизмов, которые легли бы в их основу.Было много работы!60 тысяч строк кода.1 000 типов.

Теперь я хочу сериализовать любую часть производственной линии в XML без необходимости писать собственный код сериализации для каждого компонента в производственной линии.Я хочу единого подхода, проведенного в базовом классе.Пока что в этом нет ничего необычного.

Но есть загвоздка в том, что C # (ну, .NET)

  1. Невозможно сериализовать словари
  2. Невозможно сериализовать интерфейсы
  3. Предлагает ограниченную отладку, ведение журналов и проверку в сериализации 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;
    }

Итак

  1. Является ли этот подход правильным?
  2. Можно ли его улучшить или сократить?
  3. Кто-то уже опубликовал лучшее решение?
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...