4

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;
BrianKE
  • 4,035
  • 13
  • 65
  • 115
  • What is the type `T` in the actual implementation in the last code snippet? Is it `List` or something different? – Codor May 28 '14 at 19:44
  • @Codor It would be something further up the XML chain like . MyConfiguration class would then have a List> ScheduledServices. – BrianKE May 28 '14 at 19:48
  • You mean that `MyConfiguration` would have a member `ScheduledServices` of type `List>`? For what argument `T`? Please show the definition. – Codor May 28 '14 at 19:51
  • @Codor The is what I am stuck on. I thought I could do a List> where T is defined by the inheriting class (i.e., in "public AlphaService : ScheduledService" T would be AlphaService) – BrianKE May 28 '14 at 19:55

1 Answers1

3

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.

Community
  • 1
  • 1
John Arlen
  • 6,539
  • 2
  • 33
  • 42
  • So even though & extend ScheduledService I cannot create List> – BrianKE May 28 '14 at 19:46
  • Thank you so much for the example. I think I almost have it. The serializer does not error out but no ScheduledServices are added to the List. I have added my exact code I use for deserializing to the OP. Do you see any issue with the way I am deserializing that would keep the ScheduledServices from loading? – BrianKE May 28 '14 at 20:56
  • @John_Arlen I finally was able to get this to work using the example you gave and the information from the link. I have posted my solution as an edit to the OP in case anyone else comes across this problem. – BrianKE May 29 '14 at 11:42