Click here to Skip to main content
15,891,657 members
Articles / Desktop Programming / Windows Forms

Generic XML Serialization Methods

Rate me:
Please Sign up or sign in to vote.
4.11/5 (7 votes)
29 Apr 2009CPOL3 min read 46.9K   400   28   5
An XML Helper Class that can Serialize and De-Serialize custom User objects using C# Generics

Introduction 

My search for an XML Wrapper to serialize and de-serialize my objects was fruitless. Apparently the internet has little that can do this, unless you want to use a complete Object state management solution like Karvonite which as with any external solution will come with its overheads, bugs, and what not.

What I was looking for was intuitively simple, an XML Save | Load method that I could re-use, for any object type. Andrew Ma from Devhood, wrote an article that solved the problem, for a specified object type.

However it does not take into account the following:

  1. Ability to automatically name XML file based on the type of the object. This avoids the programmer from having to map his XML file names to the object he’s trying to serialize.
  2. The ability to pass in any object to the XML methods to serialize and de-serialize without worrying about its type (provided of course if the object is serializable, if all of its fields that are to be serialized are marked with appropriate XMLAttribute such as  [XmlAttribute("Password")] for a class variable of name “password”. Refer to Figure 1.)

    Correctly Serialized Class with XML Attributes

    Figure 1: Correctly Serialized Class (with XmlAttribute)

  3. To persist state of the XmlSerializer (Namespace: System.Xml.Serialization) without having to re-initialize it for the Serialize or De-Serialize methods. 

In order to achieve the above objectives, I utilized a C# feature called Generics to provide Type independency, in order to utilize my XML Helper class for any serializable object, in any C# application.

Background 

Using the Code  

To run the code, you will need Visual Studio 2008 Express. All the corresponding imports and references have been made.  

Test Class (To be Serialized into XML)

The main class file I used to test is called UserList which includes primarily a System.Collection.ArrayList of a class of Users which include in turn a bunch of primitive C# data types.

This is a good example to test the automatic serialization of what is arguably a complex data structure / class.

C#
// UserList.cs 

using System;
using System.Collections;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace TESTXML
{
    // Shopping list class which will be serialized
    [XmlRoot("UserList")]
    public class UserList
    {
        private ArrayList myUserList;

        public UserList()
        {
            myUserList = new ArrayList();
        }              

        [XmlElement("myUsers")]
        public User[] myUsers
        {
            get
            {
                User[] myUsers = new User[myUserList.Count];
                myUserList.CopyTo(myUsers);
                return myUsers;
            }
            set
            {
                if (value == null) 
                    return;
                User[] myUsers = (User[])value;
                myUserList.Clear();
                foreach (User myUser in myUsers)
                    myUserList.Add(myUser);
            }
        }

        public string AddItem(User myUser)
        {
            myUserList.Add(myUser);
            return myUser.ToString();
        }
    }
}		

Test Sub Class (To Also Be Automatically Serialized into XML)

C#
//User.cs 

public class User
{
    [XmlAttribute("UserName")]
    public String UserName = "";

    [XmlAttribute("Email")]
    public String Email = "";

    [XmlAttribute("Password")]
    public String Password = "";

    [XmlAttribute("TotalScore")]
    public int TotalScore = 0;

    [XmlAttribute("IsApproved")]
    public Boolean IsApproved = false;

    public User(string myUserName, String myEmail, String myPassword, 
				int myTotalScore, Boolean myIsApproved)
    {
        UserName = myUserName;
        Email = myEmail;
        Password = myPassword;
        TotalScore = myTotalScore;
        IsApproved = myIsApproved;
    }

    public override string ToString()
    {
        return "UserName: " + UserName + ", Email: " + Email + ", 
		TotalScore = " + TotalScore + ", IsApproved = " + IsApproved;
    }
} 

Main XML Helper Class that Serializes | Deserializes Any Serializable Object of Any Class Passed to it

C#
//Main File: XMLHelper.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Collections;

namespace TESTXML
{
   public class XMLHelper <T>
    {
       XmlSerializer mySerializer ;
       String ClassName;
       String BaseDirectory;
       
       public XMLHelper()
       {
           mySerializer = new XmlSerializer(typeof(T));
           BaseDirectory = "";
       }

       public XMLHelper(String myBaseDirectory)
       {
           mySerializer = new XmlSerializer(typeof(T));
           BaseDirectory = myBaseDirectory;
       }

       public String FileName(T myObj)
       {
           ClassName = myObj.GetType().Name;
           return BaseDirectory + @"\" + ClassName + ".xml";           
       }

       public void Save(T myObj)
        {
            TextWriter myWriter = new StreamWriter(FileName(myObj));
            mySerializer.Serialize(myWriter, myObj);
            myWriter.Close();
        }

        public T Load(T myObj)
        {
            XmlSerializer mySerializer = new XmlSerializer(typeof(T));
            TextReader myReader = new StreamReader(FileName(myObj));
            T NewObject = (T)mySerializer.Deserialize(myReader);
            myReader.Close();
            return NewObject;
        }        
    }
}

Points of Interest

C#
public String FileName(T myObj)
{
    ClassName = myObj.GetType().Name;
    return BaseDirectory + "\\" + ClassName + ".xml";           
}

Note in the above, how I extracted the Object File Name, using the GetType method, and a conceptual Reflection concept to generate automatically an ***.xml file name for the corresponding object, in the appropriate Base Directory.

This increases the level of re-usability by one, while decreasing one more thing a programmer needs to take care of, i.e. naming convention with which to map XML file names on disk to user objects. A neat little tweak.  

Using the Code

Initialize the BaseDirectory to your XML file Path Location respect to the Project Default Output directory. 

Instantiate a new XMLHelper class with the Type of the object to be invoked, in this case just the class name.

At any point later, reload the same from the XMLHelper class. (Note: The XMLHelper class can be re-instantiated, all you would need is the base directory, which should eventually be made a project setting anyway.)

C#
UserList myListOfUsers = new UserList();     
String BaseDirectory = "Data/XML"
XMLHelper<UserList>myXMLHelper = new XMLHelper<UserList>(BaseDirectory);
myListOfUsers = null;
C#
//Load From BaseDirectory
myListOfUsers = (UserList)myXMLHelper.Load(myListOfUsers);

Results  

Create / Load Test object initializes UserList with two Users In it

Serialize object serializes the just created UserList object into XML and writes it to Disk, while resetting the object.

De-Serialize object gets the FilePath of the object (by simply accepting as an argument the Object type as shown above in the FileName function), reads it from disk and into memory thereby repopulating the object of class UserList.

The process is complete.

Image 2

Figure 2: Successful Test of Serialization De-Serialization of UserList Class

You can download the accompanying source code and all files from the link at the top of this article.

License

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


Written By
Software Developer (Senior) Microsoft
United States United States
**Update**: I now work at Microsoft. See more here: https://kanhar.github.io/

*Update*: I now work as a software developer at Investment Bank, working on mostly proprietary stuff.

Kanhar Munshi currently works as a consultant for the New York City Department of Health and Environmental Surveillance building, deploying and maintaining ASP.NET web applications, while also serving in the role of an assistant database administrator.

He is currently involved in the building of a Large Scale implementation of a Normalized Data Store, Data Warehouse for the Department of Health.

His interests, include C#, running, table tennis, triathlons and going on long day hikes.

Comments and Discussions

 
SuggestionA proposal of a slightly different solution Pin
Member 1505463024-Jan-21 2:36
Member 1505463024-Jan-21 2:36 
Questionok, but express? Pin
Donsw12-Jun-09 17:33
Donsw12-Jun-09 17:33 
AnswerRe: ok, but express? Pin
Kanhar Munshi19-Jun-09 10:13
Kanhar Munshi19-Jun-09 10:13 
GeneralYour Load(T) method should be parameterless Pin
Omer Mor1-May-09 4:59
Omer Mor1-May-09 4:59 
The only purpose of the T argument is to be passed to the FileName method.
And the FileName method uses this argument just to get it's type. However the type is know at compile time! It's <t>.
So you should be using typeof(T).Name instead of myObj.GetType().Name and drop that useless myObj argument.
GeneralMy vote of 2 Pin
Samer Aburabie28-Apr-09 20:35
Samer Aburabie28-Apr-09 20:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.