Monday, May 28, 2007

Xml serialization of enums - System.InvalidOperationException

A few days ago I was writing a web service when all of a sudden a test failed on me with:
System.InvalidOperationException: Instance validation error: '3' is not a valid value for SomeEnumInMyApp
which turned out to be quite a google-unfriendly exception. The exception is thrown when trying to serialize an enum with a value that isn't explicitly in the declaration of the enum. A couple of people dealt with this, but I don't really want to manually edit the WSDL, and I don't care right now about breaking a future version of the web service (not likely to happen in my case)
In case nothing of this makes any sense to you, let's see some code:
Given this enum declaration:
public enum Epopo {
 Uno = 1,
 Dos = 2
}
"Epopo?" Yes, Epopo. Just because.
This test passes:
[Test]
public void SerializeEnumToXml1() {
 Epopo p = Epopo.Uno;
 using (MemoryStream ms = new MemoryStream()) {
  XmlSerializer xml = new XmlSerializer(typeof(Epopo));
  xml.Serialize(ms, p); 
 }
}
But this one fails with the mentioned exception:
[Test]
public void SerializeEnumToXml2() {
 Epopo p = Epopo.Uno | Epopo.Dos;
 using (MemoryStream ms = new MemoryStream()) {
  XmlSerializer xml = new XmlSerializer(typeof(Epopo));
  xml.Serialize(ms, p); 
 }
}
Hmm. What if we try with a LosFormatter instead of XmlSerializer?
[Test]
public void SerializeEnumToLosFormatter() {
 Epopo p = Epopo.Uno | Epopo.Dos;
 LosFormatter los = new LosFormatter();
 using (MemoryStream ms = new MemoryStream()) {
  los.Serialize(ms, p); 
 }
}
Test passes. Damn you, XmlSerializer. What about XmlEnum?
public enum XmlEpopo {
 [XmlEnum("Uno")]
 Uno = 1,
 [XmlEnum("Dos")]
 Dos = 2
}

[Test] 
public void SerializeEnumWithXmlEnumToXml() { 
 XmlEpopo p = XmlEpopo.Uno | XmlEpopo.Dos; 
 using (MemoryStream ms = new MemoryStream()) { 
  XmlSerializer xml = new XmlSerializer(typeof(XmlEpopo));
  xml.Serialize(ms, p); 
 }
}
Doesn't work either... Well, after that I despaired a bit and started trying everything: XmlElement, PowerCollections.Set, Iesi.Collections.ListSet, Dictionary<Epopo, object>, but nothing worked. Ultimately, I used a List<Epopo>, like this:
[Test]
public void SerializeListOfEnum() { 
 List<Epopo> l = new List<Epopo>();
 l.Add(Epopo.Uno); 
 l.Add(Epopo.Dos); 
 using (MemoryStream ms = new MemoryStream()) { 
  XmlSerializer xml = new XmlSerializer(typeof(List<Epopo>));
  xml.Serialize(ms, l); 
 } 
}
It sucks, I know, but at least it works. If any knows a better solution, feel free to post a comment!
By the way, it seems that Microsoft is having this same problem as well...
Full code for these tests is here.

4 comments:

Jen said...

I was experiencing this same issue when I neglected to decorate my enum with the [Flags] attribute. Without it, the ToString operator will output the raw bitwise value as an int (unless your enum inherits byte). Since the combination of any two values wouldn't be recognized during deserialization, they're just letting us know up front during validation. Thanks Microsoft?

mausch said...

Didn't know about the Flags attribute! thanks!

sanghamitra said...

I got this issue and its very simple if you declare a enum and also assign value like Enum Status {
pendingAdd=1,
pendingDelete=2
}
then always keep in mind , wherever you use this enum type in datatmember , one of these values should be assigned. if the value is not assigned to the field then the default value of any enum which is 0 (zero) will be assigned and you will end up with this exception...

David Vella said...

Thanks!!! [Flags] did the trick!!