Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
472 views
in Technique[技术] by (71.8m points)

c# - Serializing a class with a generic Enum that can be different Enum types

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; } 
    }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...