In C#, how do I use an XmlSerializer
to deserialize an object that might be of a base class, or of any of several derived classes without knowing the type beforehand?
All of my derived classes add additional data members. I've made a simple GUI that can serialize and deserialize class objects. It will serialize objects as whatever inherited class (or even just the base class) is appropriate based on which fields the user chooses to populate.
I have no issues with the serialization; the problem is the deserialization. How can I possibly have the XmlSerializer
deserialize data to the correct derived class without knowing the class beforehand? I currently create an XmlReader
to read the first node of the XML file and determine the class from it, and it seems to work for my purposes, but it seems like an extremely inelegant solution.
I've posted some sample code below. Any suggestions?
BaseType objectOfConcern = new BaseType();
XmlSerializer xserializer;
XmlTextReader xtextreader = new XmlTextReader(DEFAULT_FILENAME);
do { xtextreader.Read(); } while (xtextreader.NodeType != XmlNodeType.Element);
string objectType = xtextreader.Name;
xtextreader.Close();
FileStream fstream = new FileStream(DEFAULT_FILENAME, FileMode.Open);
switch (objectType)
{
case "type1":
xserializer = new XmlSerializer(typeof(DerivedType));
objectOfConcern = (DerivedType)xserializer.Deserialize(fstream);
//Load fields specific to that derived type here
whatever = (objectOfConcern as DerivedType).NoOfstreamubordinates.ToString();
case "xxx_1":
//code here
case "xxx_2":
//code here
case "xxx_n":
//code here
//and so forth
case "BaseType":
xserializer = new XmlSerializer(typeof(BaseType));
AssignEventHandler(xserializer);
objectOfConcern = (BaseType)xserializer.Deserialize(fstream);
}
//Assign all deserialized values from base class common to all derived classes here
//Close the FileStream
fstream.Close();
I recently wrote this generic serializer\deserializer for base class T and any derived classes of T. Seems to work so far.
The Type[] array stores all the derived types of T and T itself. The deserializer tries each of them, and returns when it found the right one.
Have you some root class/tag which contains that derived types? If yes, you can use XmlElementAttribute to map tag name to type:
you can use XmlInclude
otherwise if you want to add the types when serializing:
If you're not set upon using the
XmlSerializer
you can use theDataContractSerializer
with theKnownType
attribute instead.All you need to do is add a
KnownType
attribute to the parent class for each sub class and theDataContractSerializer
will do the rest.The
DataContractSerializer
will add type information when serializing to xml and use that type information when deserializing to create the correct type.For example the following code:
Will output :
In the output you'll notice the xml for c2 and c3 contained extra type information which allowed the
DataContractSerializer.ReadObject
to create the correct type.You could try to use the constructor XmlSerializer(Type type, Type[] extraTypes) to create a serializer that works with all involved types.