Introduction
Consider an XML file that contains tags representing classes in your code, each of these classes having their own attributes. There is no specific order for these tags to be listed, and sometimes, some tags will not be listed within the XML file. How would you read such a file without the need to hard-code those attributes, or use switch
or if
statements?
This article will demonstrate how to do that without the need to hard-code anything using System.Reflection
methods.
Using the Code
Consider the following XML file, which represents the following class structure: a class Vehicle
that is a parent class for the child classes Aircraft
, Car
, Bus
, Motorcycle
, and Ship
representing different types of vehicles, each having its own attributes.
<Vehicle>
<Aircraft>
<Vehicle vehicleType="Presidential" Id="1"
Speed="800" Altitude="20000" NumberOfEngines="2" />
</Aircraft>
<Car>
<Vehicle vehicleType="None" Id="2" Speed="280" />
</Car>
<Car>
<Vehicle vehicleType="Private" Id="3" Speed="220" />
</Car>
<Aircraft>
<Vehicle vehicleType="Private" Id="4"
Speed="790" Altitude="30900" NumberOfEngines="4" />
</Aircraft>
<Bus>
<Vehicle vehicleType="Public" Id="5"
Speed="150" NumberOfPassengers="60" />
</Bus>
<Motorcycle>
<Vehicle vehicleType="Private" Id="6"
Speed="300" Racer="true" />
</Motorcycle>
<Motorcycle>
<Vehicle vehicleType="Government" Id="7"
Speed="180" Racer="false" />
</Motorcycle>
<Bus>
<Vehicle vehicleType="Government" Id="8"
Speed="190" NumberOfPassengers="48" />
</Bus>
<Ship>
<Vehicle vehicleType="None" Id="9"
Speed="50" Weight="10000" Steam="false" />
</Ship>
</Vehicle>
Now, we need to read this XML file and construct an object for each vehicle node and then save all of these objects in some data structure.
I used a dictionary of DataElement
s as follows:
private readonly IDictionary<int, DataElement> vehicleDictionary =
new Dictionary<int, DataElement>();
where each DataElement
contains two properties: one for the VehicleType
and the other for the BaseVehicle
object, as follows:
public class DataElement
{
public BaseVehicle Vehicle
{
get;
set;
}
public VehicleType VehicleType
{
get;
set;
}
}
The VehicleType
is an Enum that represents types of vehicles such as presidential, private, public etc.
Now for the core of this article. I used an XmlDocument
object to access the XML file and carry out XML operations on it. To use that, we need to first load the XML file, and then read all tags included within the <Vehicle>
tag.
XmlDocument doc = new XmlDocument();
try
{
doc.Load("Vehicles.xml");
}
catch (Exception)
{
throw new Exception("File not found.");
}
XmlNodeList temp = doc.GetElementsByTagName("Vehicle").Item(0).ChildNodes;
After that, I used an Assembly
object (an object that contains the intermediate code, resources, and the metadata for a certain type of class, and is available under the System.Reflection
namespace) to maintain the assembly of BaseVehicle
.
Assembly asm = typeof(BaseVehicle).Assembly;
After that, I loop on every tag within the XmlDocument
object, performing the following:
- Getting the
Type
of the corresponding class for the current node name:
Type tp = asm.GetType("GenericXML.Vehicle." + node.Name);
Getting all vehicles within the current node (of the current type):
XmlNodeList itemsOfType = node.ChildNodes;
Looping on those nodes one by one, while automatically creating objects to be used to fill the dictionary. Those objects are still empty, i.e., their attributes are not set, so I'll show now how to determine those values:
object obj = Activator.CreateInstance(tp);
DataElement tempElement = new DataElement
{
VehicleType = (VehicleType)Enum.Parse(typeof (VehicleType),
itemsNode.Attributes["vehicleType"].Value),
Vehicle = (BaseVehicle)obj
};
For setting the suitable attributes for each object created, I used Reflection again, but this time to access class attributes and have the accessibility to set them as well. To do that, I used the GetProperty().SetValue()
method.
tp.GetProperty(att.Name).SetValue(tempElement.Vehicle, Convert.ChangeType
(att.Value, tp.GetProperty(att.Name).PropertyType), null);
Points of Interest
Now, the thing you should mind is the naming of the attributes in the XML file; names must match with, taking in mind, the case of letters. Either that, or just convert the attribute names to lower (or upper) case and compare.
Please feel free to inquire about anything that is not clear enough.