views:

358

answers:

3

I have been reading around but I have not come across a solution to my problem

I am currently working with a Business Object that will hold all my data and we need to convert this object to and from XML.

My object holds a list of Actions (List...), but there are 2 action types (for now). I have to action types SimpleAction and CompositeAction, and they both inherit from IAction allowing them to both be held in the Actions list.

Now you can probably see the problem as Interfaces cannot be serialized as they hold no data.

How, with maybe some sample code, do I write a Class or Serializer that gets that object type and performs then serializes object with the correct type?

Some code:

  [XmlArray("Actions")]
  public List<IAction> Actions { get; set; }

  public interface IAction
   {
     int ID { get; set; }

     ParameterCollection Parameters { get; set; }

     List<SectionEntity> Validation { get; set; }

     TestResultEntity Result { get; set; }

     string Exception { get; set; }
   }

[XmlType("A")]
public class SimpleActionEntity : IAction
{
    #region IAction Members

    [XmlAttribute("ID")]
    public int ID { get; set; }

    [XmlIgnore]
    public ParameterCollection Parameters { get; set; }

    [XmlIgnore]
    public List<SectionEntity> Validation { get; set; }

    [XmlIgnore]
    public TestResultEntity Result { get; set; }

    [XmlElement("Exception")]
    public string Exception { get; set; }

    #endregion
}

Any help would be greatly appreciated. :)

+1  A: 

You can use XmlArrayItemAttribute, As we discussed it's no good to create a list of IAction so it's good to create base class

public interface IAction {}
public abstract class ActionBase : IAction {}
public class SimpleAction : ActionBase {}
public class ComplexAction : ActionBase {}


[XmlArray("Actions")]
[XmlArrayItem(typeof(SimpleAction)),XmlArrayItem(typeof(ComplexAction))]
public List<ActionBase> Actions { get; set; }

In fact you also can control Element names in the xml file like this:

  [XmlArray("Actions")]
  [XmlArrayItem(typeof(SimpleAction),ElementName = "A")]
  [XmlArrayItem(typeof(ComplexAction),ElementName = "B")]
  public List<ActionBase> Actions { get; set; }
Mike
When I designed the object structure I thought you could do this, but when I try to serialize I get an inner exception saying that I cannot serialize an interface.
Oliver
You can redesign object model to use abstract class that implement interface IAction and then inherit all Action classes from an abstract class.
Mike
That is pretty much what John said below
Oliver
I've updated my answer to reflect our discussion. You need XmlArrayItem attribute in this case to serialize both SimpleAction and ComplexAction with all their serializable fields, not only with base class fields.
Mike
A: 

Maybe if you derive your two action classes from a common abstract base class that implements the interface?

public interface IAction {}
public abstract class ActionBase : IAction {}
public class SimpleAction : ActionBase {}
public class ComplexAction : ActionBase {}

Then instead of using List<IAction>, use List<ActionBase>.

John Saunders
I have also tried this but perhaps I implemented it wrong because when the serializer to the list it only serialized the base class and this behaviour is not useful as there are a number of additional properties in the ComplexAction
Oliver
A: 

Ok I have created a solution that I feels does what I want pretty well.

What I did is rather than holding

 [XmlArray("Actions")]
 public List<IAction> Actions { get; set; }

I decided to create an ActionsCollection class that handled teh List BUT also allowed me to use IXMLSerializable to override the ReadXml and WriteXML methods so that I could handle the way the list is Serialized and Deserialized.

[XmlElement("Actions")]
public ActionCollection Actions { get; set; }

public class ActionCollection: CollectionBase, IXmlSerializable
{
    #region IList Members
      ...
    #endregion

    #region ICollection Members
     ...
    #endregion

    #region IEnumerable Members
    ...
    #endregion

    #region IXmlSerializable Members

    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
       //TODO
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        foreach (IAction oAction in List)
        {       
                XmlSerializer s = new XmlSerializer(oAction.GetType());
                s.Serialize(writer, oAction);
        }
    }

    #endregion
}
Oliver
Good. I think putting [XmlInclude("SimpleAction")][XmlInclude("ComplexAction")] would also have worked.
John Saunders