2

I'm trying to design an application that will allow the user to specify an Enum type in an XML, and from that the application will execute a specific method tied to that enum (using a dictionary). I'm getting hung up on the Enum portion of the XML.

public class TESTCLASS
{
    private Enum _MethodType;

    [XmlElement(Order = 1, ElementName = "MethodType")]
    public Enum MethodType
    {
        get { return _MethodType; }
        set { _MethodType = value; } 
    }
    public TESTCLASS() { }

    public TESTCLASS(Enummies.BigMethods bigM)
    {
        MethodType = bigM;
    }
    public TESTCLASS(Enummies.SmallMethods smallM)
    {
        MethodType = smallM;
    }
}

public class Enummies
{
    public enum BigMethods { BIG_ONE, BIG_TWO, BIG_THREE }
    public enum SmallMethods { SMALL_ONE, SMALL_TWO, SMALL_THREE }
}

And then trying to serialize the TESTCLASS results in an exception:

string p = "C:\\testclass.xml";
TESTCLASS testclass = new TESTCLASS(Enummies.BigMethods.BIG_ONE);
TestSerializer<TESTCLASS>.Serialize(p, testclass);

System.InvalidOperationException: The type Enummies+BigMethods may not be used in this context.

My serialization method looks like this:

public class TestSerializer<T> where T: class
{
    public static void Serialize(string path, T type)
    {
        var serializer = new XmlSerializer(type.GetType());
        using (var writer = new FileStream(path, FileMode.Create))
        {
            serializer.Serialize(writer, type);
        }
    }

    public static T Deserialize(string path)
    {
        T type;
        var serializer = new XmlSerializer(typeof(T));
        using (var reader = XmlReader.Create(path))
        {
            type = serializer.Deserialize(reader) as T;
        }
        return type;
    }
}

I tried including some checking/casting in the MethodType Getter, but this results in the same error.

    public Enum MethodType
    {
        get 
        { 
            if (_MethodType is Enummies.BigMethods) return (Enummies.BigMethods)_MethodType; 
            if (_MethodType is Enummies.SmallMethods) return (Enummies.SmallMethods)_MethodType;
            throw new Exception("UNKNOWN ENUMMIES TYPE");
        }
        set { _MethodType = value; } 
    }
Talen Kylon
  • 1,908
  • 7
  • 32
  • 60

1 Answers1

2

When I try to serialize your class with XmlSerializer, the innermost exception I get is:

Message="System.Enum is an unsupported type. Please use [XmlIgnore] attribute to exclude members of this type from serialization graph."

This is self-explanatory: you cannot serialize a member whose type is the abstract type System.Enum.

You can, however, serialize a member of type System.Object provided that all possible types of value that might be encountered are declared statically by using [XmlInclude(typeof(T))]. Thus you can modify your type as follows:

// Include all possible types of Enum that might be serialized
[XmlInclude(typeof(Enummies.BigMethods))]
[XmlInclude(typeof(Enummies.SmallMethods))]
public class TESTCLASS
{
    private Enum _MethodType;

    // Surrogate object property for MethodObject required by XmlSerializer
    [XmlElement(Order = 1, ElementName = "MethodType")]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public object MethodTypeObject
    {
        get { return MethodType; }
        set { MethodType = (Enum)value; }
    }

    // Ignore the Enum member that cannot be serialized directly
    [XmlIgnore]
    public Enum MethodType
    {
        get { return _MethodType; }
        set { _MethodType = value; }
    }
    public TESTCLASS() { }

    public TESTCLASS(Enummies.BigMethods bigM)
    {
        MethodType = bigM;
    }
    public TESTCLASS(Enummies.SmallMethods smallM)
    {
        MethodType = smallM;
    }
}

And XML will be generated as follows:

<TESTCLASS xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <MethodType xsi:type="BigMethods">BIG_THREE</MethodType>
</TESTCLASS>

Or

<?xml version="1.0" encoding="utf-16"?>
<TESTCLASS xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <MethodType xsi:type="SmallMethods">SMALL_TWO</MethodType>
</TESTCLASS>

Notice the xsi:type attribute? That is a W3C standard attribute that an element may use to explicitly assert its type. Microsoft uses this attribute to represent type information for polymorphic elements as explained here.

Sample fiddle.

You might want to check that the value type is a known Enum type in the setter for MethodObject (rather than the getter) but this is not required for XML serialization.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Awesome. I didn't realize you can serialize members of System.Object as long as you specify the potential types in XmlIncludes. I wonder why I was getting a different exception than what you saw. Also, are these attributes necessary? I've never seen them before. "[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]" – Talen Kylon May 13 '17 at 18:11
  • 1
    @TalenKylon - those attributes aren't necessary. They keep the surrogate property from showing up in the debugger and property editor. The exception I listed was the innermost attribute rather than the outer attribute. Also, I added the `[XmlInclude(typeof(Enummies.BigMethods))]` attributes before testing serialization. – dbc May 13 '17 at 18:14