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();
XmlSerializer enables you to control how objects are encoded into XML. The XmlSerializer enables you to control how objects are encoded into XML, it has a number of constructors.
The XmlSerializer creates C# (. cs) files and compiles them into . dll files in the directory named by the TEMP environment variable; serialization occurs with those DLLs. These serialization assemblies can be generated in advance and signed by using the SGen.exe tool.
Yes, you can tell the XmlSerializer to ignore namespaces during de-serialization.
Serialization is a process by which an object's state is transformed in some serial data format, such as XML or binary format. Deserialization, on the other hand, is used to convert the byte of data, such as XML or binary data, to object type.
Have you some root class/tag which contains that derived types? If yes, you can use XmlElementAttribute to map tag name to type:
public class RootElementClass { [XmlElement(ElementName = "Derived1", Type = typeof(Derived1BaseType))] [XmlElement(ElementName = "Derived2", Type = typeof(Derived2BaseType))] [XmlElement(ElementName = "Derived3", Type = typeof(Derived3BaseType))] public BaseType MyProperty { get; set; } } public class BaseType { } public class Derived1BaseType : BaseType { } public class Derived2BaseType : BaseType { } public class Derived3BaseType : BaseType { }
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.
/// <summary> /// A generic serializer\deserializer /// </summary> /// <typeparam name="T"></typeparam> public static class Serializer<T> { /// <summary> /// serialize an instance to xml /// </summary> /// <param name="instance"> instance to serialize </param> /// <returns> instance as xml string </returns> public static string Serialize(T instance) { StringBuilder sb = new StringBuilder(); XmlWriterSettings settings = new XmlWriterSettings(); using (XmlWriter writer = XmlWriter.Create(sb, settings)) { XmlSerializer serializer = new XmlSerializer(instance.GetType()); serializer.Serialize(writer, instance); } return sb.ToString(); } /// <summary> /// deserialize an xml into an instance /// </summary> /// <param name="xml"> xml string </param> /// <returns> instance </returns> public static T Deserialize(string xml) { using (XmlReader reader = XmlReader.Create(new StringReader(xml))) { foreach (Type t in types) { XmlSerializer serializer = new XmlSerializer(t); if (serializer.CanDeserialize(reader)) return (T)serializer.Deserialize(reader); } } return default(T); } /// <summary> /// store all derived types of T: /// is used in deserialization /// </summary> private static Type[] types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(s => s.GetTypes()) .Where(t => typeof(T).IsAssignableFrom(t) && t.IsClass && !t.IsGenericType) .ToArray(); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With