XmlSerializer with new enum values

We make extensive use of xml serialization / deserialization in our project to transfer data between multiple applications. We have a regular xsd, which we will generate with C # classes, then use the XmlSerializer to move from xml to objects and vice versa.

The problem is updating one application to add new enumeration values, but the other application has not yet been updated. Now an application that is not updating tries to deserialize xml and fails because it does not know about the new enumeration.

If we have app1 and app2, everything works correctly in the field, then app2 is updated with the new enum value in xsd and updated by the client in the field. Suddenly, app1 breaks because it does not know about enum, app1 may not even use this enum field, it does not affect application1, but it still breaks.

Are there any known ways around this. Basically, what I want to do is determine what to do when the enumeration is not found, use the default value, or list as an invalid type and set it to null.

Both XmlSerializer and DataContractSerializer exceptions exclude this situation.

I reviewed the YAXLib XML serialization project ( http://www.codeproject.com/KB/XML/yaxlib.aspx ), this also throws an exception, but there is source code and can be changed. This project uses different property attributes and will require considerable changes, but it is probably possible.

Any other suggestions.

+4
source share
7 answers

Unfortunately, there is no way to control the deserialization of enum values ​​... As a workaround, you can serialize the enum values ​​as a string:

[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 } 
+9
source

For future reference, it is best to use XmlEnumAttribute , which tells XMLSerializer what name each enumeration value for serialization and deserialization has.

 public enum EmployeeStatus { [XmlEnum(Name = "Single")] One, [XmlEnum(Name = "Double")] Two, [XmlEnum(Name = "Triple")] Three } 
+8
source

I am also struggling with this problem, and I found a partial solution using XmlAttributeOverrides. According to MS documentation:

You can override the value of the Name property for XmlEnumAttribute by creating an instance of the XmlEnumAttribute class and assigning it to the XmlEnum property of the XmlAttributes object. See the XmlAttributeOverrides class for more details.

So, I did this:

  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); 

The only problem I discovered with this approach is that I cannot specify multiple values ​​for one enumeration ...

+2
source

I recently ran into this problem with DataContractSerializer. In principle, the process of deserializing an enumeration is not forgiving in this regard, which makes it almost impossible to manage backward compatibility.

As a job, I decided to use the support field and handle the enum transformation myself.

 [DataMember(Name="AddressType")] private string _addressType { get; set; } public AddressType AddressType { get { AddressType result; Enum.TryParse(_addressType, out result); return result; } } 

A private field can be deserialized using a DataContractSerializer, but a public field is required for the XmlSerializer.

+2
source

You can make a manufacturer application aware of several versions of consuming applications and use different namespaces for each version, both XML and C #. There should be some coordination between the applications between the agreements in order to agree on which scheme they will follow, and there is an additional responsibility for extending the application so that it is backward compatible with all possible active consumer applications.

+1
source

Use C # serialization with object versions this will allow you to handle various situations that arise when updating one application, and another - not

0
source

It is best to split the serialization / deserialization code between the two applications. But if this is not possible, you can use XmlAttributeOverrides to control how the xml will be deserialized quite widely. For example, below some old xml version we want to deserialize

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

In the style property of the new version, it is encoded as an enumeration of type Style

  public enum Style { Style1, Style2, Style3, } 

If we try to deserialize the old xml, we get an exception. We can use XmlAttributeOverrides to avoid this.

  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
source

Source: https://habr.com/ru/post/1264797/


All Articles