9

I am using the XMLSerializer to save this class to a file. The class has a string and an enum as shown below:

public class IOPoint
{
     string Name {get; set;}
     TypeEnum {get; set;}
}


public enum TypeEnum
{
    Temperature,
    Pressure,
    Humidity,
}

When serialized it looks like this.

<IOPoint>
  <Name>Relative Humidity</Name>
  <TypeEnum>Humidity</TypeEnum>
</IOPoint>

I've been serializing and deserializing this object with no problems for several versions. I no longer want to support Humidity, so I removed it from the enum. However, this causes an exception when deserializing from XML because the value in the TypeEnum field, Humidity, is not a valid value for the TypeEnum. This makes sense, but how to handle this?

What I'd like to do is just ignore this error. And leave the value as null. I've tried implementing the OnUnknownElement XmlDeserilizationEvent class. Unfortunately, this does not catch this error.

Any ideas on how to catch and ignore this error (I can clean up after the deserialization is complete).

Mitch

Mitch
  • 1,173
  • 1
  • 10
  • 15
  • If you found a solution I'd love to know. I have a related problem where the server-side is including a new enum flag value that the client doesn't know about, so really want to find a way to manage serialization of just that one field. Next time I'll just use an int, but for now... backward compatibility. – avenmore Jan 17 '18 at 06:21

4 Answers4

5

You can mark the member Obsolete

public enum TypeEnum
{
    Temperature,
    Pressure,
    [Obsolete]
    Humidity
}
msmucker0527
  • 5,164
  • 2
  • 22
  • 36
  • 2
    The reason I can't use the [Obsolete] attribute is because it is common practice in our code to iterate through the enum values. So even if "Humidity" is marked obsolete, it would still be picked up in our foreach loops. (Unless there is a way to override the enumerator?) – Mitch May 22 '12 at 21:49
  • Can you make the variable null wherever you are using it? e.g., TypeEnum? val = null; –  Sep 16 '16 at 19:46
4

It’s generally considered bad practice to remove an enumeration member after your library is already in use. Why don’t you leave the member in place, but mark it with the [Obsolete] attribute to prevent future use? Specifying the ObsoleteAttribute(string,bool) constructor’s second parameter as true would cause a compile-time error if the marked member is accessed.

public enum TypeEnum
{
    Temperature,
    Pressure,

    [Obsolete("It's always sunny in Philadelphia", true)]
    Humidity,
}

To circumvent the error when checking deserialized values, you could compare against the underlying value: typeEnum == (TypeEnum)2.

Douglas
  • 53,759
  • 13
  • 140
  • 188
2

You can use attributes to change node names around and hide elements from xml serialization, parsing just that one element manually:

public class IOPoint
{
 public string Name {get; set;}

 [XmlIgnore]
 public TypeEnum TypeEnum {get; set;}

 [XmlElement("TypeEnum")]
 public string LegacyTypeEnum 
 {
  get { return this.TypeEnum.ToString(); }
  set 
  { 
   try
   {
    this.TypeEnum = (TypeEnum)Enum.Parse(typeof(TypeEnum),value);
   }
   catch(ArgumentException)
   {
    // Handle "Humidity"
   }
   catch(OverflowException)
   {
   }
  }
 }
}

Per comments there appears to be some confusion; here is a worked example as a Visual Studio 2010 project. This approach is a simple way of manually parsing only one property of an object (still leveraging the XmlSerializer to do the XML parsing).

user423430
  • 3,654
  • 3
  • 26
  • 22
  • Serializing the enum value as a string instead of the enum, would be a good idea if I didn't already have XML files I need to read. These existing files have the element and I still need to read the value of the TypeEnum field and act accordingly. An ideal solution would be to catch the exception when it tries to deserialize the Humidity value. But none of the exception handlers you put on the deserializer catch this error. – Mitch May 23 '12 at 13:50
  • I've included a compilable sample to demonstrate both that it works and how it works. – user423430 May 31 '12 at 12:05
1

You could implement IXmlSerializable, where you can use something like TryParse for the enum.

But I agree with the other posters using the Obsolete attribute.

rumpelstiefel
  • 466
  • 3
  • 5
  • If I can't find another workaround, custom serialization using IXMLSerializable my be the only solution. While I'm at it, I would also add a Version field which I could use to guide the deserialization. We do something similar when using ISerializable. The real class has about 15 fields. I wonder if I could get away with just writing deserializer code for the one field? – Mitch May 23 '12 at 13:56