Source URL:http://www.dotnetjohn.com/articles.aspx?articleid=173
XML Serialization Using C#...
Object serialization is a process through which an object's state is transformed into some serial data format, such as XML or a binary format.
By: Nitin Pande Date: December 5, 2004
Printer Friendly Version
Object Serialization is a process through which an object's state is transformed into some serial data format, such as XML or a binary format. For example, you can serialize an object and transport it over the Internet using HTTP between a client and a server. On the other end, deserialization reconstructs the object from the stream. XML was designed to be a technology for data exchange across heterogeneous systems. It can be easily transmitted between distributed components because of its platform independence and its simple, text-based, self-describing format. In this article I will discuss the serialization of custom business objects to XML and reconstruction the object back from the XML string, using C#. This process is called XML serialization and is very well supported by .Net.
Some good uses for serialization/deserialization include:
- Storing user preferences in an object.
- Maintaining security information across pages and applications.
- Modification of XML documents without using the DOM.
- Passing an object from one application to another.
- Passing an object from one domain to another.
- Passing an object through a firewall as an XML string.
These are only a few of the many possibilities that serialization opens up for us.
Suppose we have an object defined and instantiated as shown below:
class Person
{
private String personName;
private Int32 personAge;
public String Name
{
get
{
return personName;
}
set
{
personName = value ;
}
}
public Int32 Age
{
get
{
return personAge;
}
set
{
personAge = value ;
}
}
}
Listing 1: Demo class having public properties
Instantiation of the class is shown below:
Person oPerson = new Person();
oPerson.Name = "Anthony" ;
oPerson.Age = 38;
Let's say that for some reason, we wanted to save a copy of this object just as it is at this very moment. We could serialize it as an XML document that would look something like this:
<Person >
<Name >Anthony</Name >
<Age >38</Age >
</Person >
Then, at some later time when we needed to use the object again, we could just deserialize it and have our object restored to us just as it was at the moment we serialized it.
However XML serialization follows certain rules and the classes defined by us have to conform to these rules.
- XML serialization serializes only the public fields and property values of an object into an XML stream.
- XML serialization does not include type information.
- XML serialization requires a default constructor to be declared in the class that is to be serialized.
- XML serialization requires all properties that are to be serialized as read write properties. Read only properties are not serialized.
Serialize class
The .Net framework provides a rich collection of classes for serialization. As we are concerned with XML serialization, we will examine the use of the XmlSerializer class. This is the central class for XML serialization and its most important methods are the Serialize and Deserialize methods. Most of the articles discussing XML serialization use IO streams for saving serialized data into XML files, however in my example I will demonstrate the use of XML serialization to convert an object into XML string and store it in a memory stream instead of an XML file. This technique is extremely useful in cases where a complex object is to be converted to XML string and its properties have to be manipulated and then the object recreated from the XML string on the fly!!
Attributes
Before we start with the process of demonstrating the technique of XML serialization let us have a small discussion on Metadata Attributes. A complete discussion of Metadata Attributes is beyond the scope of this article. Nevertheless we need to understand the concepts behind metadata attributes to understand how XML serialization works. Attributes are annotations to an interface or a class definition to specify certain behavior. Assemblies, classes, fields and methods, each can have attributes. Some are used by the compiler, some are used by the runtime, e.g. to identify a method requires a call to a web service, or how to serialize a class to XML. There is very little overhead associated when using attributes. Attaching attributes to a class is done directly in the source code. The syntax to initialize a metadata attribute and attach it to a class or a method in C# is either:
[Attribute(constructor-parameters-list )]
or:
[Attribute(constructor-parameters-list, property=value, … )]
The System.Xml.Serialization namespace introduces a set of attributes to control how classes are mapped to XML. Let's look at a quick example: One of the attributes used with XML serialization is the XmlRootAttribute to change the name of the root element of a serialization hierarchy. You would add the XmlRootAttribute to a class like this:
using System.Xml.Serialization;
[XmlRootAttribute(Name="Human", IsNullable=false)]
public class Person
{
// class implementation goes here
}
If we make this change in the class declaration of listing 1, and serialize the instance of this class we will get the XML generated resembling the one shown below:
<Human>
<Name>Anthony</Name>
<Age>38</Age>
</Human>
We would be using UTF-8 encoding for the creating the XML stream for the custom object as it supports a wide range of Unicode character values and surrogates. For this purpose, we will make use of the UTF8Encoding class provided by the .Net framework. For more information on UTF8Encoding you may want to refer to MSDN at:
For explaining the concept of XML serialization I will create and use a demo class Animal with a number of properties that describe an animal such as name, length, weight, place of existence etc. First create a C# console application using the Visual Studio.Net and name the project as MySerializer . This will automatically create a namespace MySerializer. Import the following namespaces into the project:
- System.Xml.Serialization
- System.IO
- System.Text
Now add a new class and name it Animal. The code for this class is given below:
using System;
namespace MySerializer
{
/// <summary>
/// Base class for the animal object hierarchy
/// </summary>
[XmlRootAttribute(ElementName="WildAnimal" , IsNullable=false )]
public class Animal
{
public Animal()
{
//default constructor
}
private String animalName;
private String foodTypeCategory;
private Boolean isDomesticed;
private String placeOfExistence;
private Int32 length;
private Int32 height;
private Int32 weight;
public String AnimalName
{
get
{
return animalName;
}
set
{
animalName = value ;
}
}
public String FoodTypeCategory
{
get
{
return foodTypeCategory;
}
set
{
foodTypeCategory = value ;
}
}
public Boolean IsDomesticed
{
get
{
return isDomesticed;
}
set
{
isDomesticed = value ;
}
}
public String PlaceOfExistence
{
get
{
return placeOfExistence;
}
set
{
placeOfExistence = value ;
}
}
public Int32 Length
{
get
{
return length;
}
set
{
length = value ;
}
}
public Int32 Height
{
get
{
return height;
}
set
{
height = value ;
}
}
public Int32 Weight
{
get
{
return weight;
}
set
{
weight = value ;
}
}
}
}
Listing 2: Custom class whose objects are to be serialized to XML
When you create the C# console application a default class by the name of Class1 gets added to the project. This class contains the definition of the Main function that is the entry point of the assembly. Rename this class from “Class1” to clsMain. In this class clsMain, we will define the custom methods to serialize and deserialize the object of the class Animal. Apart from these there are helper methods to convert a UTF-8 byte array to string and vice – versa.
The definition of these methods is given below:
/// <summary>
/// To convert a Byte Array of Unicode values (UTF-8 encoded) to a complete String.
/// </summary>
/// <param name="characters"> Unicode Byte Array to be converted to String </param>
/// <returns> String converted from Unicode Byte Array </returns>
private String UTF8ByteArrayToString ( Byte [ ] characters )
{
UTF8Encoding encoding = new UTF8Encoding ( );
String constructedString = encoding.GetString ( characters );
return ( constructedString );
}
/// <summary>
/// Converts the String to UTF8 Byte array and is used in De serialization
/// </summary>
/// <param name="pXmlString"></param>
/// <returns></returns>
private Byte [ ] StringToUTF8ByteArray ( String pXmlString )
{
UTF8Encoding encoding = new UTF8Encoding ( );
Byte [ ] byteArray = encoding.GetBytes ( pXmlString );
return byteArray;
}
Listing 3: Helper Methods
Serialization
Now we will add a custom method SerializeObject to the class clsMain which will use the XmlSerializer to serialize the object to XML string. The code for this method is given below:
/// <summary>
/// Method to convert a custom Object to XML string
/// </summary>
/// <param name="pObject"> Object that is to be serialized to XML </param>
/// <returns> XML string </returns>
public String SerializeObject ( Object pObject )
{
try
{
String XmlizedString = null ;
MemoryStream memoryStream = new MemoryStream ( );
XmlSerializer xs = new XmlSerializer ( typeof ( Animal ) );
XmlTextWriter xmlTextWriter = new XmlTextWriter ( memoryStream, Encoding.UTF8 );
xs.Serialize ( xmlTextWriter, pObject );
memoryStream = ( MemoryStream ) xmlTextWriter.BaseStream;
XmlizedString = UTF8ByteArrayToString ( memoryStream.ToArray ( ) );
return XmlizedString;
}
catch ( Exception e )
{
System.Console .WriteLine ( e );
return null ;
}
}
Listing 4: Method to serialize the custom business object
Let us look through the code carefully. First we create an instance of the MemoryStream class since we intend to keep the XML string in memory instead of saving it to a file. Instantiate the XmlSerializer class with the type of the class that is to be serialized. In our case this is the class Animal. Now we use the XmlTextWriter Class that provides a fast, non-cached, forward-only way of generating streams or files containing XML data. Notice that we use an overloaded constructor of the XmlTextWriter class and set the encoding to UTF-8. This means that the XML document will be created with UTF-8 encoding. Now, we go to the most important step and that is calling one of the overloaded implementations of the Serialize method of the XmlSerializer object. We now read the XML stream generated by the serializer from the BaseStream of the XmlTextWriter . Since the BaseStream property returned by the XmlTextWriter is of the type System.IO.Stream, we can easily type cast it into MemoryStream (which is derived from System.IO.Stream) for our purpose.
We now pass the Byte[] array returned by the ToArray() method of the MemoryStream object to the helper function UTF8ByteArrayToString . This method constructs the XML string from the binary array and returns the same. This small piece of trick is performed by the overloaded implementation of the GetString() method of the UTF8Encoding class. The output that is generated is given below:
<?xml version="1.0" encoding="utf-8"?>
<WildAnimal xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AnimalName>Lion</AnimalName>
<FoodTypeCategory>Carnivore</FoodTypeCategory>
<IsDomesticed>false</IsDomesticed>
<PlaceOfExistence>Africa</PlaceOfExistence>
<Length>15</Length>
<Height>4</Height>
<Weight>900</Weight>
</WildAnimal>
Notice that all the properties defined in the class Animal are serialized into nodes and the name of the root also gets changed from Animal to WildAnimal, this is because we had included the attribute XmlRootAttribute in the declaration of the class Animal.
Deserialization
Just as we had created a custom method to serialize the object, we will create another method in the class clsMain which will use the Deserialize() method of the XmlSerializer class to recreate the object from the XML string. The code for this method is given below:
/// <summary>
/// Method to reconstruct an Object from XML string
/// </summary>
/// <param name="pXmlizedString"></param>
/// <returns></returns>
public Object DeserializeObject ( String pXmlizedString )
{
XmlSerializer xs = new XmlSerializer ( typeof ( Automobile ) );
MemoryStream memoryStream = new MemoryStream ( StringToUTF8ByteArray ( pXmlizedString ) );
XmlTextWriter xmlTextWriter = new XmlTextWriter ( memoryStream, Encoding.UTF8 );
return xs.Deserialize ( memoryStream );
}
To get the Animal object back from the XML string you will simply call this method and pass the XML string. It will first convert the XML string to a byte array (exactly the reverse of what we did in serialization) and then recreate the object from the XmlSerializer.
There you are!! You have now learnt the entire process of serializing a custom object into an XML string and keeping it in an in memory variable and then reconstructing it back using deserialization. With the help of powerful XMLDocument class and other rich XML parsing classes and readers (such as XmlReader) provided by .Net you can change values or nodes , change attributes and apply advanced XML processing functions to XML document.
Happy Programming!