0

I've got an xml file with a collection that looks like this

<AbstractCollection>
    <ConcreteA name="Bob" age="4"/>
    <ConcreteB name="Tree" size="1" />
</AbstractCollection>

I know that if I use a concrete collection - i.e. the two elements were the same type - it would be easy to use the standard XML deserialisation in C#. The different types seem to make it more difficult.

Is there a way to use the simple xml deserialisation to get this out, or do I have to implement deserialisation myself?

[Added for clarity] I should add that the xml already exists and I can't change it. I'm receiving messages in xml that take a form like that above. A more concrete example would be:

<Actions>
    <Walked name="Bob" distance="4"/>
    <Cycled name="Jane" gear="3rd" />
</Actions>

What I want to end up with is a "Cycled" and a "Walked" object. Oh, and to make it extra fun, the order is important.

Also, I've tried using XmlInclude attributes in the code, but that works by changing the xml when you serialise it (unless I've been using it wrong of course).

mike1952
  • 493
  • 5
  • 12
  • You can't have different types stored in a single collection instance. But maybe I'm missing something, can you provide more details on the AbstractCollection? – mclaassen Mar 25 '13 at 19:12
  • You can have different types stored in a single collection, but only if they are sub types of whatever the collection is. @mike1952, what do you mean by "seems to make it more difficult"; what have you tried? what did it do/ in what way didn't it work? – Immortal Blue Mar 25 '13 at 19:28
  • Hi Immortal Blue and mclaassen, I've edited the question above to add clarity and show what I've tried - basically just using XmlInclude, which is the normal way to serialise inherited collections of objects. My problem is that it's already serialised. "Seems to make it more difficult" is my understated way of saying "I can't work it out without having to write my own deserializer." – mike1952 Mar 26 '13 at 08:38

1 Answers1

1

So I found the solution, thanks to this answer.

The key was to use XmlElementAttribute to change the way that the different classes were serialized.

The only strange part was that I found I had to create a wrapper class for the list, or else the Cycled and Walked elements aren't wrapped in a root element.

I know that the namespace stuff is still there, but I can deal with that - the problem of getting the derived classes has been solved!

Thanks to everyone who chipped in.

[Serializable]
public class Actions
{
    [XmlElementAttribute("Walked", typeof(WalkedAction))]
    [XmlElementAttribute("Cycled", typeof(CycledAction))]
    public List<Action> ActionList { get; set; }
}
[Serializable]
public abstract class Action
{
    [XmlAttribute]
    public string Name { get; set; }
}

[Serializable]
public class WalkedAction : Action
{
    [XmlAttribute]
    public int Distance { get; set; }
}

[Serializable]
public class CycledAction : Action
{
    [XmlAttribute]
    public string Gear { get; set; }
}


public class Program
{
    public static void Main(string[] args)
    {
        var actionList = new Actions();
        actionList.ActionList = new List<Action>();
        actionList.ActionList.Add(new WalkedAction { Name = "Bob", Distance = 4 });
        actionList.ActionList.Add(new CycledAction { Name = "Jane", Gear = "3rd" });

        var ser = new XmlSerializer(typeof(Actions));

        TextWriter w = new StringWriter();
        ser.Serialize(w, actionList);

        TextReader r = new StringReader(w.ToString());
        Actions result = (Actions) ser.Deserialize(r);
    }
}
Community
  • 1
  • 1
mike1952
  • 493
  • 5
  • 12