user853710
user853710

Reputation: 1767

C# Deserialize XML to a list of ChildClasses

I have a XML from a 3rd party software that I have to deserialize, but I have the spec of the XML.

At a certain point I have a node that contains a bunch of different Nodes of different types. I have to deserialize this Node (Vehicles) as a list. Each of the child nodes is one subclass of the class Vehicle

public List<Vehicle> Vehicles = new List<Vehicles>();

the definition of the class is as following

public class Vehicle
{
    public string Vehicletype { get; set; }

    public virtual bool Drive() { return true; }
}

public class Car:Vehicle
{
    public int NumberOfDoors { get; set; }

    public override bool Drive() { return false; }
}

public class Boat:Vehicle
{
    public int NumberOfSeats { get; set; }
}

The definition of the XML looks like this

<vehicles>
    <car NumberOfDoors="4" />
    <car NumberOfDoors="3" />
    <boat NumberOfSeats="1024" />
    <boat NumberOfSeats="20" />
    <car NumberOfDoors="5" />
</vehicles>

At a certain point I have to loop through them and let them "Drive". I don't have the experience With XMLSchema definitions. Now I am using The vehicleType in a factory and some other things. It's basically becoming a mess.

I would need a suggestion how to let the Serializer do this instead of me. I doubt that I am the first one to have this issue.

Upvotes: 2

Views: 121

Answers (1)

dbc
dbc

Reputation: 116794

Firstly, you have to mark NumberofSeats and NumberOfDoors with the XmlAttribute attribute to tell XmlSerializer that these properties should appear as XML attributes:

public class Car : Vehicle
{
    [XmlAttribute]
    public int NumberOfDoors { get; set; }

    public override bool Drive() { return false; }
}

public class Boat : Vehicle
{
    [XmlAttribute]
    public int NumberOfSeats { get; set; }
}

Next, if <vehicles> appears as an XML Node inside some containing root element, you can make your public List<Vehicle> Vehicles appear as a member inside the corresponding container class, and apply an XmlArray attribute to it to indicate that the list should be serialized in two levels, and XmlArrayItem attributes to inform XmlSerializer of the possible of vehicles that might be encountered in the list along with the start tag to use for each:

public class VehiclesContainer // Your container class
{
    [XmlArray("vehicles")]
    [XmlArrayItem("vehicle", typeof(Vehicle))]
    [XmlArrayItem("car", typeof(Car))]
    [XmlArrayItem("boat", typeof(Boat))]
    public List<Vehicle> Vehicles = new List<Vehicle>();
}

On the other hand, <vehicles> is the root node of the XML document and there is no containing node, you can introduce a root node class containing the list of vehicles, then apply multiple XmlElement attributes to indicate that the list should be serialized in one level rather than two along with the start tag to use for each possible vehicle type:

[XmlRoot("vehicles")]
public class VehicleList
{
    [XmlElement("vehicle", typeof(Vehicle))]
    [XmlElement("car", typeof(Car))]
    [XmlElement("boat", typeof(Boat))]
    public List<Vehicle> Vehicles = new List<Vehicle>();
}

Having done this, XmlSerializer will automatically deserialize your class hierarchy.

For more information, see here.

Upvotes: 2

Related Questions