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:
- 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.
- 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.)
Figure 1: Correctly Serialized Class (with XmlAttribute)
- 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 User
s 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.
using System;
using System.Collections;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace TESTXML
{
[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)
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
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
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.)
UserList myListOfUsers = new UserList();
String BaseDirectory = "Data/XML"
XMLHelper<UserList>myXMLHelper = new XMLHelper<UserList>(BaseDirectory);
myListOfUsers = null;
myListOfUsers = (UserList)myXMLHelper.Load(myListOfUsers);
Results
Create / Load Test object initializes UserList
with two User
s 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.
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.