Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using XML Serialization to Read Custom Configuration Elements

0.00/5 (No votes)
5 May 2004 1  
Custom configuration is a powerful tool provided by the .NET Framework. Ideas such as the Provider Model make heavy use of the configuration techniques. This article will demonstrate using XML Serialization to place objects into your configuration that can be used at run time.

Introduction

This article will provide a little trick for you in dealing with custom configuration sections. Think of it this way - since you're working on an object-oriented fashion with languages within the .NET Framework, and since most of the time you'll be returning XML data your apps will use to create objects your application can use, it stands to reason that using the XmlSerializer object to read the XML data directly from your config files into objects within your application is a logical "next step". This article will give a quick introduction to the idea behind XML serialization, and provide you with a starting point to use in your own applications.

Getting Started with an Example Object

To get started in this explanation, let's take a look at an example object we'll use. To make everything as simple as possible with a logical-enough example object, we'll create a new Person object, and give it some pretty minimal properties. The code below outlines this object. Note especially the attributes placed over the class and its properties.

/// <summary>

 /// Example class we'll place into the 

 /// configuration file for reading.

 /// </summary>

 [XmlRoot("Person")]
 public class Person
 {
  string __firstName = String.Empty;
  string __lastName = String.Empty; 
  public Person()
  {
  } 
  [XmlAttribute(DataType = "string", AttributeName = "Firstname")]
  public string Firstname
  {
   get { return __firstName; }
   set { __firstName = value; }
  } 
  [XmlAttribute(DataType = "string", AttributeName = "Lastname")]
  public string Lastname
  {
   get { return __lastName; }
   set { __lastName = value; }
  }
 }

Pretty simple really. This class' structure is relatively basic. We've used the XmlRoot attribute to specify that the root node of a Person object, when serialized, will be "Person". Likewise, we want to represent the two properties as attributes of that root node. The resulting XML code would look something like that below.

<Person Firstname="Brady" Lastname="Gaster"/>

The Configuration Class

Since we're talking about configuration in this article, it stands to reason that we'll need to create a custom configuration reader class, which we'll need to inherit from the interface IConfigurationSectionHandler. This class is relatively simple; implementation will need to be added to the Create() method. In this implementation, the code will iterate over a particular node within the application (or web) configuration file and inspect each of the child nodes within it.

For each node located in this XmlNodeList collection, the node is read into an XmlNodeReader object, which is then passed into an instance of an XmlSerializer class. This instance then deserializes the XmlNode into an instance of a Person object. Finally, the new object is added into a local variable - an array of Person objects - via the internal Add method. Once all of the nodes have been deserialized back into their object state, the array is returned to the calling code. The code sample below demonstrates this implementation.

/// <summary>

 /// The custom configuration class. This will read the custom 

 /// configuration section and for each child node, return an

 /// instance of the object, deserialized. 

 /// </summary>

 public class BasicConfigurator : IConfigurationSectionHandler
 {
  Person[] __people = new Person[0]; 
  public object Create(object parent, 
   object configContext, 
   System.Xml.XmlNode section)
  {
   XmlNodeList nodes = section.SelectNodes("//Person");
   for(int i=0; i<nodes.Count; i++)
   {
    XmlNodeReader rdr = new XmlNodeReader(nodes[i]);
    XmlSerializer ser = new XmlSerializer(typeof(Person));
    Person p = (Person)ser.Deserialize(rdr);
    Add(p);
   }
   return __people;
  } 
  /// <summary>

  /// Adds a Person to the local array. 

  /// </summary>

  /// <param name="p"></param>

  void Add(Person p)
  {
   Person[] tmp = new Person[__people.Length+1];
   Array.Copy(__people,0,tmp,0,__people.Length);
   tmp[__people.Length] = p;
   __people = tmp;
  }
 }

The PersonFactory Helper Class

As pointed out in the MSDN documentation, it's not a great idea to return objects directly from the Create method. For this reason, a helper class has been added to this code to simplify the retrieval of the collection of Person objects. This class, the PersonFactory, does the simple job of using the custom configuration reader implementation and then returning Person array retrieved from the configuration file to the caller.

/// <summary>

 /// The person factory, which will return the list 

 /// of people from the configuration file. 

 /// </summary>

 public class PersonFactory
 {
  public static Person[] GetProple()
  {
   return (Person[])ConfigurationSettings.GetConfig("People");
  }
 }

Finally, the Config File Structure.

At this point, the code is relatively complete; the last thing needed is for the application to be configured properly so that the PersonFactory can be used within the code. Without properly adding the configSections element, the custom tags are pretty much useless. The XML code from the client application contained with the source download is shown below:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <configSections>
  <section name="People" 
   type="XmlSerializationConfig.BasicConfigurator, 
    XmlSerializationConfig" />
 </configSections>
 <People>
  <Person Firstname="Brady" Lastname="Gaster"/>
  <Person Firstname="Christian" Lastname="Conrad"/>
  <Person Firstname="Gina" Lastname="Ostrea"/>
 </People>
</configuration>

The first section of this config file - the configSections area - basically informs the application "for the 'People' section of this configuration file, use the 'XmlSerializationConfig.BasicConfigurator' class. It knows what to do and will help you out!" The configurator class then iterates over each of the Person nodes contained within the People node, deserializes each node into a real-life Person object, and adds it to the collection.

The final piece of code we'll take a look at is the client code itself. This code uses the PersonFactory class to get the collection of People that have been defined in the configuration.

class Client
 {
  /// <summary>

  /// The main entry point for the application.

  /// </summary>

  [STAThread]
  static void Main(string[] args)
  {
   ReadPeopleFromConfiguration(); 
   // wait to exit

   Console.ReadLine();
  } 
  /// <summary>

  /// Reads the list of people from the configuration file.

  /// </summary>

  static void ReadPeopleFromConfiguration()
  {
   Person[] personArr = PersonFactory.GetProple();
   foreach(Person p in personArr)
   {
    Console.WriteLine("{0} {1}",p.Firstname,p.Lastname);
   }
  }
 }

When this simple console application executes, you'll see that it works perfectly; for each Person you have identified within the configuration section, a Person's first and last names will be written to the screen! Not too difficult an example, but one that clearly demonstrates the point! The screenshot below demonstrates the expected results of the client's execution.

Client Execution

Now, you can use more object-oriented techniques in your configuration methodologies!

Happy coding!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here