XML Deserialize items of generic types
c#, deserialization, xml
Solution
The `XmlSerializer` class needs more information than your XML is offering to work properly.
For example, it does not know exactly which Type is referred to by "AlphaService" ("MyNamespace.AlphaService, MyUtility"?)
The easiest way to start is create a sample object in memory and call XmlSerializer.Serialize(myObj) to understand the actual format it is looking for.
If you have no control over the incoming XML, you will either need to further decorate your classes (With XmlElementAttribute, for example), or use a different mechanism
EDIT: An example of marking up your class might be:
[XmlInclude( typeof( AlphaService ) )]
[XmlInclude( typeof( BetaService ) )]
public abstract class ScheduledService : ScheduledServiceBase<ScheduledService> {...}
And you can test this by serializing it to XML and back into a concrete object:
private void SerializeAndBack()
{
var item = new ScheduledServiceHost
{
ScheduledServices = new List<ScheduledService>
{
new AlphaService {Alpha_Age = 32, Alpha_Name = "John Jones", ServiceName = "FirstService"},
new BetaService {Beta_Company = "Ajax Inc.", Beta_IsOpen = true, ServiceName = "SecondService"},
}
};
var xmlText = XmlSerializeToString( item );
var newObj = XmlDeserializeFromString( xmlText, typeof( ScheduledServiceHost ) );
}
public static string XmlSerializeToString( object objectInstance, params Type[] extraTypes )
{
var sb = new StringBuilder();
using ( TextWriter writer = new StringWriter( sb ) )
{
var serializer = new XmlSerializer( objectInstance.GetType(), extraTypes );
serializer.Serialize( writer, objectInstance );
}
return sb.ToString();
}
public static object XmlDeserializeFromString( string objectData, Type type )
{
using ( var reader = new StringReader( objectData ) )
{
return new XmlSerializer( type ).Deserialize(reader);
}
}
Here is a good SO answer on some options for marking up a class is.
Problem
Assume I have the following class: ``` public abstract class ScheduledService : ScheduledServiceBase<ScheduledService> { public CronInfo CronInfo; public String ServiceName; public ScheduledService() { } } public abstract class ScheduledServiceBase<T> { public ScheduledServiceBase() { } public virtual void StartUp(IScheduler scheduler, ScheduledService service, Dictionary<string, object> parameters = null) { ... } } ``` From this base class I create two inheriting classes like so: ``` public AlphaService : ScheduledService { public String Alpha_Name; public Int32 Alpha_Age; public AlphaService() { } } public BetaService : ScheduledService { public String Beta_Company; public Boolean Beta_IsOpen; public AlphaService() { } } ``` Then in my XML I define the two Services: ``` <ScheduledServices> <AlphaService> <Alpha_Name>John Jones</Alpha_Name> <Alpha_Age>32</Alpha_Age> <ServiceName>FirstService</ServiceName> <CronInfo>0 0 0/2 * * ? *</CronInfo> </AlphaService> <BetaService> <Beta_Company>Ajax Inc.</Beta_Company> <Beta_IsOpen>Ajax Inc.</Beta_IsOpen> <ServiceName>SecondService</ServiceName> <CronInfo>0 30 0/5 * * ? *</CronInfo> </BetaService> </ScheduledService> ``` When I deserialize this I am trying to assign the ScheduledServices to `List<ScheduledService> ScheduledServices` but nothing exists in the list after deserialization. The deserializer doesn't error out, it just doesn't create any services. Is what I am trying to do valid or do I need to setup the services differently? Here is what I am using to deserialize the XML: ``` public Boolean Load(params Type[] extraTypes) { deserializedObject = default(T); XmlTextReader reader = null; try { reader = new XmlTextReader(new StreamReader(_actualPath)); XmlSerializer serializer = new XmlSerializer(typeof(T), extraTypes); deserializedObject = (T)(serializer.Deserialize(reader)); } catch (Exception ex) { log.Error("Error: " + ex.Message); log.Debug("Stack: " + ex.StackTrace); log.Debug("InnerException: " + ex.InnerException.Message); } finally { if (reader != null) reader.Close(); } return ((this.deserializedObject != null) && deserializedObject is T); } ``` SOLUTION Thanks to John Arlen and his example and link to another post I was able to do what I wanted by creating the list as follows: ``` [XmlArrayItem("AlphaJob", Type=typeof(AlphaJob))] [XmlArrayItem("BetaJob", Type=typeof(BetaJob))] public List<ScheduledService> ScheduledServices; ```