Десериализация C# XML со значениями по умолчанию

У меня есть объект, который сериализуется/десериализуется через XmlSerializer в C#, .NET 3.5. Одно из свойств (и больше в будущем) — это коллекция: список, где T — значение перечисления. Это сериализует/десериализует нормально.

Мы также используем механизм «значений по умолчанию», чтобы предоставить значения по умолчанию для объекта, если в сериализованной версии не установлено какое-либо значение. в качестве простого примера, вот что мы делаем:

public enum MyEnum {
  Value1,
  Value2
}

public class Foo
{

  public List SomeSetting{ get; set; }

  public Foo()
  {
    SomeSetting = new List();
    SomeSetting.Add(MyEnum.Value1);
    SomeSetting.Add(MyEnum.Value2);
  }
}

Этот код отлично работает для установки значений SomeSetting по умолчанию при создании объекта.

Однако когда мы десериализуем XML-файл, содержащий значения для SomeSetting, эта установка значения по умолчанию вызывает проблемы: десериализатор xml не «сбрасывает» коллекцию SomeSetting — он не стирает ее и не заполняет новыми данными. Скорее, он добавляет к уже имеющимся данным. Итак, если в xml-файле сериализовано Value1, когда я десериализую этот файл, я получаю SomeSettings с {Value1, Value2, Value1} в качестве сохраняемых значений.

Мне нужен способ для процесса десериализации xml, позволяющий моим значениям по умолчанию существовать, когда в документе xml нет данных для SomeSetting, а также для оптовой замены значений SomeSetting, когда в документе xml есть данные. Как я могу это сделать?

К вашему сведению — это не единственное свойство в документе. Документ существует и сериализуется/десериализуется для других «простых» значений. Однако это свойство вызывает проблемы. Я должен поддержать этот сценарий, потому что мне нужно сделать это сейчас.


person Derick Bailey    schedule 28.12.2009    source источник
comment
Я думаю, что интерфейс IXmlSerializable даст мне то, что я хочу... у кого-нибудь есть предложение, как использовать этот интерфейс для решения моей проблемы?   -  person Derick Bailey    schedule 28.12.2009
comment
Другим вариантом может быть вместо самостоятельного разбора stackoverflow.com/questions/14768052/   -  person surya    schedule 08.02.2013


Ответы (3)


К вашему сведению, я решил это с помощью интерфейса IXMLSerializable. Обратите внимание, что этот код очень специфичен для моих потребностей в этом классе, поэтому YMMV.



        public void WriteXml(XmlWriter writer)
        {
            foreach (PropertyInfo prop in GetType().GetProperties())
            {
                XmlIgnoreAttribute attr;
                if (prop.TryGetAttribute(out attr))
                    continue;

                if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition().Equals(typeof(List)))
                {
                    XmlSerializer serializer = new XmlSerializer(prop.PropertyType, new XmlRootAttribute(prop.Name));
                    serializer.Serialize(writer, prop.GetValue(this, null));
                }
                else
                {
                    writer.WriteElementString(prop.Name, prop.GetValue(this, null).ToString());
                }
            }
        }

        public void ReadXml(XmlReader reader)
        {
            if (reader.IsEmptyElement)
                return;

            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(reader);

            Type type = GetType();

            foreach (XmlNode node in xDoc.DocumentElement.ChildNodes)
            {
                PropertyInfo prop = type.GetProperty(node.Name);
                if (prop != null && prop.CanWrite)
                {
                    object value;
                    if (prop.PropertyType.IsEnum)
                    {
                        string stringValue = node.InnerText;
                        value = Enum.Parse(prop.PropertyType, stringValue);
                    }
                    else if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition().Equals(typeof(List)))
                    {
                        Type enumType = prop.PropertyType.GetGenericArguments()[0];
                        value = Activator.CreateInstance(prop.PropertyType);
                        var addMethod = value.GetType().GetMethod("Add");
                        foreach (XmlNode subNode in node.ChildNodes)
                        {
                            object enumValue = Enum.Parse(enumType, subNode.InnerText);
                            addMethod.Invoke(value, new[] { enumValue });
                        }
                    }
                    else
                    {
                        string stringValue = node.InnerText;
                        value = Convert.ChangeType(stringValue, prop.PropertyType);
                    }
                    prop.SetValue(this, value, null);
                }
            }
        }

        public XmlSchema GetSchema()
        {
            return null;
        }
person Derick Bailey    schedule 30.12.2009

Быстро и грязно реализовать ISupportInitialize. Все сериализаторы .NET (IIRC) учитывают этот интерфейс и будут вызывать EndInit после десериализации.

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

person Community    schedule 28.12.2009
comment
похоже, не работает... методы из этого интерфейса не вызываются сериализатором xml. - person Derick Bailey; 28.12.2009

Еще один вариант, вместо того, чтобы самостоятельно анализировать XML, мы могли бы использовать подход, который использовал я. Просто предложение.

Десериализация C# Xml плюс предложения по дизайну

person surya    schedule 08.02.2013