There are a few approaches you can take depending on how exactly you've achieved your polymorphism in the XML itself.
Element name is the type name (reflection approach)
You could get the root element name like this:
string rootElement = null;
using (XmlReader reader = XmlReader.Create(xmlFileName))
{
while (reader.Read())
{
// We won't have to read much of the file to find the root element as it will be the first one found
if (reader.NodeType == XmlNodeType.Element)
{
rootElement = reader.Name;
break;
}
}
}
Then you could find the type by reflection like this (adjust reflection as necessary if your classes are in a different assembly):
var serializableType = Type.GetType("MyApp." + rootElement);
var serializer = new XmlSerializer(serializableType);
You would be advised to cache the mapping from the element name to the XML serializer if performance is important.
Element name maps to the type name
If the XML element names are different from the type names, or you don't want to do reflection, you could instead create a Dictionary
mapping from the element names in the XML to the XmlSerializer
objects, but still look-up the root element name using the snippet above.
Common root element with polymorphism through xsi:type
If your XML messages all have the same root element name, and the polymorphism is achieved by having types identified using xsi:type, then you can do something like this:
using System;
using System.Xml.Serialization;
namespace XmlTest
{
public abstract class RootElement
{
}
public class TypeA : RootElement
{
public string AData
{
get;
set;
}
}
public class TypeB : RootElement
{
public int BData
{
get;
set;
}
}
class Program
{
static void Main(string[] args)
{
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(RootElement),
new Type[]
{
typeof(TypeA),
typeof(TypeB)
});
RootElement rootElement = null;
string axml = "<RootElement xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"TypeA\"><AData>Hello A</AData></RootElement>";
string bxml = "<RootElement xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"TypeB\"><BData>1234</BData></RootElement>";
foreach (var s in new string[] { axml, bxml })
{
using (var reader = new System.IO.StringReader(s))
{
rootElement = (RootElement)serializer.Deserialize(reader);
}
TypeA a = rootElement as TypeA;
if (a != null)
{
Console.WriteLine("TypeA: {0}", a.AData);
}
else
{
TypeB b = rootElement as TypeB;
if (b != null)
{
Console.WriteLine("TypeB: {0}", b.BData);
}
else
{
Console.Error.WriteLine("Unexpected type.");
}
}
}
}
}
}
Note the second parameter to the XmlSerializer
constructor which is an array of additional types that you want the .NET serializer to know about.