XmlSerializer с новыми значениями перечисления - PullRequest
5 голосов
/ 25 октября 2009

Мы широко используем сериализацию / десериализацию xml в нашем проекте для передачи данных между несколькими приложениями. У нас есть общий xsd, из которого мы генерируем классы c #, затем используем XmlSerializer для перехода от xml к объектам и обратно.

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

Если у нас есть app1 и app2, в поле все работает правильно, то app2 обновляется новым значением перечисления в xsd и обновляется для клиента в поле. Внезапно app1 ломается, потому что не знает о enum, app1 может даже не использовать это поле enum, не влияет на app1, но все равно ломается.

Есть ли какие-либо известные способы обойти это? По сути, я хочу определить, что делать, когда перечисление не найдено, использовать значение по умолчанию или, если перечисление имеет нулевой тип, и установить его на нуль.

В этой ситуации возникают исключения как XmlSerializer, так и DataContractSerializer.

Я посмотрел на пользовательский проект сериализации xml YAXLib (http://www.codeproject.com/KB/XML/yaxlib.aspx), это также вызывает исключение, но есть исходный код и может быть изменено. Этот проект использует различные атрибуты свойств и потребует немало изменений но, вероятно, выполнимо.

Любые другие предложения.

Ответы [ 7 ]

11 голосов
/ 25 октября 2009

К сожалению, нет способа контролировать десериализацию значений перечисления ... В качестве временного решения вы можете сериализовать значения перечисления в виде строки:

[XmlIgnore]
public MyEnum MyProperty { get; set; }

[XmlElement("MyProperty")]
public string MyPropertyAsString
{
    get
    {
        return EnumToString(MyProperty);
    }
    set
    {
        MyProperty = StringToEnum<MyEnum>(value);
    }
}

public T StringToEnum<T>(string stringValue)
{
    // Manually convert the string to enum, ignoring unknown values
}

public string EnumToString<T>(T enumValue)
{
    // Convert the enum to a string
}
7 голосов
/ 21 марта 2013

Для использования в будущем лучше всего использовать XmlEnumAttribute , который сообщает XMLSerializer, какое имя имеет каждое значение перечисления для сериализации и десериализации.

public enum EmployeeStatus
{
   [XmlEnum(Name = "Single")]
   One,
   [XmlEnum(Name = "Double")]
   Two,
   [XmlEnum(Name = "Triple")]
   Three
}
2 голосов
/ 06 сентября 2013

Недавно я столкнулся с той же проблемой с DataContractSerializer. По сути, процесс десериализации enum не прощает, поэтому почти невозможно управлять обратной совместимостью.

В качестве обходного пути я решил использовать вспомогательное поле и самостоятельно обработать преобразование перечисления.

[DataMember(Name="AddressType")]
private string _addressType { get; set; }

public AddressType AddressType
{
    get
    {
        AddressType result;
        Enum.TryParse(_addressType, out result);
        return result;
    }
}

Закрытое поле может быть десериализовано DataContractSerializer, но для XmlSerializer потребуется открытое поле.

2 голосов
/ 03 февраля 2012

Я тоже боролся с этой проблемой, и я нашел частичное решение с использованием XmlAttributeOverrides. Согласно документации MS:

Вы можете переопределить значение свойства Name для XmlEnumAttribute, создав экземпляр класса XmlEnumAttribute и присвоив его свойству XmlEnum объекта XmlAttributes. Подробнее см. В классе XmlAttributeOverrides.

Итак, я сделал это:

    XmlAttributeOverrides attrOverrides =
       new XmlAttributeOverrides();
    XmlAttributes attrs = new XmlAttributes();
    XmlEnumAttribute _enum = new XmlEnumAttribute();
    _enum.Name = "new value";
    attrs.XmlEnum = _enum;
    attrOverrides.Add(typeof(EnumType), "OldValue", attrs);
    XmlSerializer s =
        new XmlSerializer(typeof(MyType), attrOverrides);

    FileStream fs = new FileStream(filename, FileMode.Open);
    MyType newObj = (MyType)s.Deserialize(fs);

Единственная проблема, с которой я столкнулся при таком подходе, заключается в том, что я не могу указать несколько значений для одного перечисления ...

1 голос
/ 25 октября 2009

Вы можете уведомить производящее приложение о нескольких версиях потребляющих приложений и использовать для каждой из этих версий разные пространства имен, как XML, так и C #. Между приложениями должна быть некоторая координация, чтобы согласовать, по какой схеме они будут следовать, и на производящем приложении лежит дополнительная ответственность за дальнейшую обратную совместимость со всеми возможными активными потребительскими приложениями.

0 голосов
/ 22 сентября 2016

Лучший способ - разделить код сериализации / десериализации между двумя приложениями. Но если это невозможно, вы можете использовать XmlAttributeOverrides, чтобы довольно широко контролировать процесс десериализации xml. Например, под какой-то старой версией xml мы хотим десериализовать

    ...
    <Shape xsi:type="Shape">
       <Style>1</Style>
    <Shape/>
    ...

В новой версии свойство стиля закодировано как перечисление типа Style

    public enum Style
    {
       Style1,
       Style2,
       Style3,
    }

Если мы попытаемся десериализовать старый xml, мы получим исключение. Мы можем использовать XmlAttributeOverrides, чтобы избежать этого

    var overrides = new XmlAttributeOverrides();
    overrides.Add(typeof(Style), "Style1", new XmlAttributes { XmlEnum = new 
        XmlEnumAttribute("1" ) } );
    overrides.Add(typeof(Style), "Style2", new XmlAttributes { XmlEnum = new      
        XmlEnumAttribute("2") });
    overrides.Add(typeof(Style), "Style3", new XmlAttributes { XmlEnum = new 
        XmlEnumAttribute("3") });

    var xmlSerializer = new XmlSerializer(typeof(ContentRootType),   
        overrides);
    var content = xmlSerializer.Deserialize(xmlReader) as ContentRootType;
0 голосов
/ 25 октября 2009

использовать c # настраиваемую сериализацию с контролем версий объектов; это позволит вам справляться с различными ситуациями, которые возникают, когда одно приложение обновляется, а другое не

...