Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

Generic/Dynamic XML file handling using Reflection and C#

3.33/5 (3 votes)
18 Aug 2010CPOL2 min read 44K   673  
This article describes how to read XML files dynamically and as generic as possible using .NET Reflection.

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.

XML
<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 DataElements as follows:

C#
// A dictionary that represents data read from the XML file
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:

C#
public class DataElement
{
    /// <summary>
    /// The BaseVehicle object indicating whether this
    /// element is a car, bus, aircraft, motorcycle or a ship.
    /// </summary>
    public BaseVehicle Vehicle
    {
        get;
        set;
    }

    /// <summary>
    /// The type of the vehicle indicated in VehicleType enumerator class.
    /// </summary>
    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.

C#
// Used to access the XML file and carry out XML operations
XmlDocument doc = new XmlDocument();
try
{
    doc.Load("Vehicles.xml");
}
catch (Exception)
{
    throw new Exception("File not found.");
}

// Holds all tags existing inside the main tag "Vehicle"
// i.e. holds all nodes in the XML file in their
// original XML format
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.

C#
// Used to hold all details of "GenericXML.Vehicle" including its child classes
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:
  • C#
    // Determines the type of the current node name
    // by searching in the assembly object "asm"
    Type tp = asm.GetType("GenericXML.Vehicle." + node.Name);
  • Getting all vehicles within the current node (of the current type):
  • C#
    // The vehicle nodes inside the current "type" node
    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:
  • C#
    // An automatically initiated object of the current type
    object obj = Activator.CreateInstance(tp); 
    DataElement tempElement = new DataElement
                      {
                          // Set the VehicleType by converting from string (in XML file) 
                          //to corresponding Enum type
                          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.

C#
// Set the value to the value mentioned in the XML file after conversion
// from string to suitable type
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)