Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use an XmlSerializer to deserialize an object that might be of a base or derived class without knowing the type beforehand?

Tags:

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(); 
like image 242
8bitcartridge Avatar asked Jan 26 '11 03:01

8bitcartridge


People also ask

Why do we use XmlSerializer class?

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.

How does the XmlSerializer work C#?

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.

Can I make XmlSerializer ignore the namespace on Deserialization?

Yes, you can tell the XmlSerializer to ignore namespaces during de-serialization.

What does deserialize XML mean?

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.


2 Answers

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 { } 
like image 115
SeeSharp Avatar answered Oct 15 '22 10:10

SeeSharp


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(); } 
like image 26
Bartel Avatar answered Oct 15 '22 09:10

Bartel