|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionUsing serialization objects are persisted which allows us to store them easily in files or databases. E.g.: Storing configuration settings of an application, storing custom settings of a user for an application, saving the state of an application. Since we have persisted forms of objects we can transfer them between applications. Deserialization is the opposite of serialization, which creates the object from its persisted form. Objects are serialized into streams during serialization. .NET supports XML, SOAP and Binary serializations. XML serialization
Creating the objectLet’s create our class for the serialization sample. public class Exam{
public Exam(){
header=new Header();
}
private Header header;
private Question[] questions;
public Header Header{
get{return header;}
set{header=value;}
}
public Question[] Questions{
get{return questions ;}
set{questions=value;}
}
}
public class Header{
public Header(){}
private string title;
public string Title{
get{return title;}
set{title=value;}
}
}
public class Question{
public Question(){}
private int id;
private string[] items;
public int ID{
get{return id;}
set{id=value;}
}
public string[] Items{
get{return items;}
set{items=value;}
}
}
And we initialize the object: Exam exam=new Exam();
exam.Header=new Header();
exam.Header.Title="Exam title";
exam.Questions=new Question[1];
exam.Questions[0]=new Question();
exam.Questions[0].ID=1;
exam.Questions[0].Title=
"What is your favourite serialization method?";
exam.Questions[0].Items=new string[2];
exam.Questions[0].Items[0]="Xml";
exam.Questions[0].Items[1]="Soap";
Serializing the object into a file using XML serializationpublic static void ToXml(Object objToXml,
string filePath,bool includeNameSpace) {
StreamWriter stWriter=null;
XmlSerializer xmlSerializer;
try {
xmlSerializer = new XmlSerializer(objToXml.GetType());
stWriter = new StreamWriter(filePath);
if (!includeNameSpace){
System.Xml.Serialization.XmlSerializerNamespaces xs=
new XmlSerializerNamespaces();
//To remove namespace and any
//other inline information tag
xs.Add("","");
xmlSerializer.Serialize(stWriter, objToXml,xs);
}
else{
xmlSerializer.Serialize(stWriter, objToXml);
}
}
catch(Exception exception){
throw exception;
}
finally {
if(stWriter!=null) stWriter.Close();
}
}
Our method takes the file path to serialize the object. We pass the object to serialize and the method creates a file serializing the object. And the file content would be: <?xml version="1.0" encoding="utf-8"?>
<Exam>
<Header>
<Title>Exam title</Title>
</Header>
<Questions>
<Question>
<ID>1</ID>
<Title>What is your favourite serialization method?</Title>
<Items>
<string>Xml</string>
<string>Soap</string>
</Items>
</Question>
</Questions>
</Exam>
DeSerializing the object from the XML fileThe following method can be used to deserialize an object from a file: public static object XmlToFromFile(string filePath,
Type type) {
XmlSerializer xmlSerializer;
FileStream fileStream=null;
try {
xmlSerializer = new XmlSerializer(type);
fileStream = new FileStream(filePath,
FileMode.Open,FileAccess.Read);
object objectFromXml=
xmlSerializer.Deserialize(fileStream );
return objectFromXml;
}
catch(Exception Ex) {
throw Ex;
}
finally {
if(fileStream!=null) fileStream.Close();
}
}
Serializing the object into a string in memory using XML serializationSometimes we need to store the serialized form of an object in memory. We can use public static string ToXml(Object objToXml,
bool includeNameSpace) {
StreamWriter stWriter=null;
XmlSerializer xmlSerializer;
string buffer;
try {
xmlSerializer =
new XmlSerializer(objToXml.GetType());
MemoryStream memStream = new MemoryStream();
stWriter = new StreamWriter(memStream);
if (!includeNameSpace){
System.Xml.Serialization.XmlSerializerNamespaces xs=
new XmlSerializerNamespaces();
//To remove namespace and any other inline
//information tag
xs.Add("","");
xmlSerializer.Serialize(stWriter, objToXml,xs);
}
else{
xmlSerializer.Serialize(stWriter, objToXml);
}
buffer = Encoding.ASCII.GetString(memStream.GetBuffer());
}
catch(Exception Ex){
throw Ex;
}
finally {
if(stWriter!=null) stWriter.Close();
}
return buffer;
}
DeSerializing the object from an XML string in memoryOf course, you will need a deserializer to convert in memory strings to objects! public static object XmlTo(string xmlString,Type type) {
XmlSerializer xmlSerializer;
MemoryStream memStream=null;
try {
xmlSerializer = new XmlSerializer(type);
byte[] bytes=new byte[xmlString.Length];
Encoding.ASCII.GetBytes(xmlString,0,xmlString.Length,bytes,0);
memStream = new MemoryStream(bytes);
object objectFromXml= xmlSerializer.Deserialize(memStream);
return objectFromXml;
}
catch(Exception Ex) {
throw Ex;
}
finally {
if(memStream!=null) memStream.Close();
}
}
Changing the classAdding new propertiesLet’s add a public class Header{
public Header(){}
private string title;
private string description;
public string Title{
get{return title;}
set{title=value;}
}
public string Description{
get{return description;}
set{description=value;}
}
}
We can still deserialize the objects without getting any errors. Values that do not exist in the serialized form are set to initial values. In this case Removing a propertyNow we want to remove the Controlling XML serialization using attributesSerialization can be controlled using attributes. A property can be marked to be excluded from serialization or it can be serialized as an XML attribute or XML element. SOAP/Binary serialization attributes differ from XML serialization attributes. The sample uses some of the attributes to explain how to control serialization. Changing the text of a property while serializingSuppose that you want to see “ [System.Xml.Serialization.XmlElementAttribute("QuestionTitle")]
public string Title{
get{return title;}
set{title=value;}
}
If you apply this attribute to an array property, it would cause the outer tag to disappear. [System.Xml.Serialization.XmlElementAttribute("Item")]
public string[] Items{
get{return items;}
set{items=value;}
}
Final output would be: <?xml version="1.0" encoding="utf-8"?>
<Exam>
<Header>
<Title>Exam title</Title>
<Description>Exam description</Description>
</Header>
<Questions>
<Question>
<ID>1</ID>
<QuestionTitle>What is your favourite serialization method?
</QuestionTitle>
<Item>Xml</Item>
<Item>Soap</Item>
</Question>
</Questions>
</Exam>
Adding an ArrayList to a classLet’s change the public class Exam{
public Exam(){
header=new Header();
questions=new ArrayList();
}
private Header header;
private ArrayList questions;
public Header Header{
get{return header;}
set{header=value;}
}
public ArrayList Questions{
get{return questions ;}
set{questions=value;}
}
}
When we run the sample we get We can specify the type in the [XmlArrayItem(typeof(Question))]
public ArrayList Questions{
get{return questions ;}
set{questions=value;}
}
Now the serialization works as it was, generating the same output to the file. Customizing the serializationSometimes we need to control XML serialization. In this case we can override serialization to change its default behaviour. We create our own Example: Removing XML version lineTo control everything during serialization Let’s write the class. It is called public class SpecialXmlWriter:XmlTextWriter
{
bool m_includeStartDocument=true;
public SpecialXmlWriter(TextWriter tw,
bool includeStartDocument):base(tw) {
m_includeStartDocument=includeStartDocument;
}
public SpecialXmlWriter(Stream sw,Encoding encoding,
bool includeStartDocument):base(sw,null) {
m_includeStartDocument=includeStartDocument;
}
public SpecialXmlWriter(string filePath,Encoding encoding,
bool includeStartDocument):base(filePath,null) {
m_includeStartDocument=includeStartDocument;
}
public override void WriteStartDocument() {
if (m_includeStartDocument) {
base.WriteStartDocument();
}
}
Now let’s write another overloaded method for serialization. In this method we used public static void ToXml(Object objToXml,
string filePath,
bool includeNameSpace,
bool includeStartDocument) {
SpecialXmlWriter stWriter=null;
XmlSerializer xmlSerializer;
try {
xmlSerializer =
new XmlSerializer(objToXml.GetType());
stWriter = new SpecialXmlWriter(filePath,null,
includeStartDocument);
System.Xml.Serialization.XmlSerializerNamespaces xs=
new XmlSerializerNamespaces();
//To remove namespace and any other
//inline information tag
xs.Add("","");
xmlSerializer.Serialize(stWriter, objToXml,xs);
}
catch(Exception Ex) {
throw Ex;
}
finally {
if(stWriter!=null) stWriter.Close();
}
}
SOAP serialization
Both SOAP and binary formatters inherit from Formatter=new BinaryFormatter();
Serializing the object into a file using SOAP serializationpublic static string ToSoap(Object objToSoap,
string filePath) {
IFormatter formatter;
FileStream fileStream=null;
string strObject="";
try{
fileStream = new FileStream(filePath,
FileMode.Create,FileAccess.Write);
formatter = new SoapFormatter();
formatter.Serialize(fileStream, objToSoap);
}
catch(Exception exception){
throw exception;
}
finally{
if (fileStream!=null) fileStream.Close();
}
return strObject;
}
Our method takes the file path and the object to serialize, and the file is created with the content shown below: <SOAP-ENV:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<a1:Exam id="ref-1"
xmlns:a1="http://schemas.microsoft.com/clr/nsassem/
SerializationSamples.SOAP/SerializationSamples%2C%20Version%3D1.0.0.0
%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<header href="#ref-3"/>
<questions href="#ref-4"/>
</a1:Exam>
<a1:Header id="ref-3"
xmlns:a1="http://schemas.microsoft.com/clr/nsassem/
SerializationSamples.SOAP/SerializationSamples%2C%20Version
%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<title id="ref-5">Exam title</title>
<description id="ref-6">Exam description</description>
</a1:Header>
<a2:ArrayList id="ref-4"
xmlns:a2="http://schemas.microsoft.com/clr/ns/System.Collections">
<_items href="#ref-7"/>
<_size>1</_size>
<_version>1</_version>
</a2:ArrayList>
<SOAP-ENC:Array id="ref-7" SOAP-ENC:arrayType="xsd:anyType[16]">
<item href="#ref-8"/>
</SOAP-ENC:Array>
<a4:Question id="ref-8"
xmlns:a4="http://schemas.microsoft.com/clr/nsassem/
SerializationSamples/SerializationSamples%2C%20Version
%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<id>1</id>
<title id="ref-9">What is your favourite serialization method?</title>
<items href="#ref-10"/>
</a4:Question>
<SOAP-ENC:Array id="ref-10" SOAP-ENC:arrayType="xsd:string[2]">
<item id="ref-11">Xml</item>
<item id="ref-12">Soap</item>
</SOAP-ENC:Array>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
DeSerializing the object from the SOAP fileThe following method can be used to deserialize an object from a SOAP file: public static object SoapToFromFile(string filePath) {
IFormatter formatter;
FileStream fileStream=null;
Object objectFromSoap=null;
try{
fileStream = new FileStream(filePath,
FileMode.Open,FileAccess.Read);
formatter = new SoapFormatter();
objectFromSoap=formatter.Deserialize(fileStream);
}
catch(Exception exception){
throw exception;
}
finally{
if (fileStream!=null) fileStream.Close();
}
return objectFromSoap;
}
Serializing the object into a string in memory using SOAP serializationpublic static string ToSoap(Object objToSoap) {
IFormatter formatter;
MemoryStream memStream=null;
string strObject="";
try{
memStream = new MemoryStream();
formatter = new SoapFormatter();
formatter.Serialize(memStream, objToSoap);
strObject =
Encoding.ASCII.GetString(memStream.GetBuffer());
//Check for the null terminator character
int index=strObject.IndexOf("\0");
if (index>0){
strObject=strObject.Substring(0,index);
}
}
catch(Exception exception){
throw exception;
}
finally{
if (memStream!=null) memStream.Close();
}
return strObject;
}
DeSerializing the object from a SOAP string in memorypublic static object SoapTo(string soapString) {
IFormatter formatter;
MemoryStream memStream=null;
Object objectFromSoap=null;
try{
byte[] bytes=new byte[soapString.Length];
Encoding.ASCII.GetBytes( soapString, 0,
soapString.Length, bytes, 0);
memStream = new MemoryStream(bytes);
formatter = new SoapFormatter();
objectFromSoap=
formatter.Deserialize(memStream);
}
catch(Exception exception){
throw exception;
}
finally{
if (memStream!=null) memStream.Close();
}
return objectFromSoap;
}
Making changes in the class before deserializingAdding a new property or changing the namespace does not effect the XML serialization. But if SOAP/Binary serialization is used, you should be very careful. SOAP/Binary serialization directly deals with members and also serializes the type information. In .NET, type information also includes the assembly name and namespace. Changing one of these may impact the application. Adding a new member or updating the members may cause problems. It may not be possible to deserialize the object back. Adding a propertyLet’s add a new property with the type of string to our [Serializable]
public class Exam{
public Exam(){
header=new Header();
questions=new ArrayList();
}
private Header header;
private ArrayList questions;
private string author;
public Header Header{
get{return header;}
set{header=value;}
}
public ArrayList Questions{
get{return questions ;}
set{questions=value;}
}
public string Author{
get{return author ;}
set{author=value;}
}
}
When we run the application, we get the following exception "An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in SerializationSamples.exe". Additional information: "Wrong number of Members. Object SerializationSamples.SOAP.Exam has 3 members, number of members deserialized is 2". SOAP serialization serializes all members of the class and looks for all of them while serializing. To overcome this problem let’s start from the beginning. We remove the Our class becomes as follows: [Serializable]
public class Exam{
public Exam(){
header=new Header();
questions=new ArrayList();
}
private Header header;
private ArrayList questions;
private Hashtable properties;
public Header Header{
get{return header;}
set{header=value;}
}
public ArrayList Questions{
get{return questions ;}
set{questions=value;}
}
}
Now add the public Exam(){
header=new Header();
questions=new ArrayList();
properties=new Hashtable();
}
private Hashtable properties;
Let’s add the public string Author{
get{
if (properties[PROPERTYNAME.AUTHOR]==null){
return "";
}
else{
return (string)properties[PROPERTYNAME.AUTHOR];
}
}
set{
properties[PROPERTYNAME.AUTHOR]=value;
}
}
And the public enum PROPERTYNAME{
AUTHOR=0
}
While adding a new property to the class, we add a new Changing the type informationWhat happens if we want to make a change in the type information of our class. Type information is also serialized while the class is serialized enabling the class to be deserialized using the type information. Type information consists of namespace, class name, assembly name, culture information, assembly version, and public key token. As long as your deserialized class and the class that is serialized reside in the same assembly it does not cause any problem. But if the serializer is in a separate assembly, .NET cannot find your class’ type hence cannot deserialize it. If you change the assembly name of your class then you get the following error that tells you .NET cannot find the associated assembly "An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in serializationsamplesa.dll". Additional information: "Parse Error, no assembly associated with XML key a1:http://schemas.microsoft.com/clr/nsassem/SerializationSamples.SOAP/ SerializationSamples%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull Exam". If you change the namespace of your class then you will get the following error that tells you .NET cannot find the associated type "An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in serializationsamples.dll". Additional information: "Parse Error, no type associated with Xml key a1 SerializationSamples.SOAP.Exam SerializationSamples, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null". If it is very likely to make changes it might be a good idea to implement In this case we can write our own class’s binder and tell the deserialization to use our binder while looking for our classes’ types. For each class that is serialized or type information recorded in the SOAP message, we need to return its type information so that the correct type can be loaded. In the last public class ExamBinder:
System.Runtime.Serialization.SerializationBinder {
public override Type BindToType(string assemblyName,
string typeName) {
string[] typeInfo=typeName.Split('.');
//The latest item is the class name
string className=typeInfo[typeInfo.Length -1];
if (className.Equals("Exam")){
return typeof (Exam);
}
else if (className.Equals("Header")){
return typeof (Header);
}
if (className.Equals("Question")){
return typeof (Question);
}
else{
return Type.GetType(string.Format( "{0}, {1}",
typeName, assemblyName));
}
}
}
Now let’s create another overloaded method for deserialization. It takes a binder parameter and passes this parameter to the formatter’s public static object SoapToFromFile(string filePath,
SerializationBinder binder) {
IFormatter formatter;
FileStream fileStream=null;
Object objectFromSoap=null;
try{
fileStream = new FileStream(filePath,
FileMode.Open, FileAccess.Read);
formatter = new SoapFormatter();
formatter.Binder=binder;
objectFromSoap=formatter.Deserialize(fileStream);
}
catch(Exception exception){
throw exception;
}
finally{
if (fileStream!=null) fileStream.Close();
}
return objectFromSoap;
}
It can be called as shown below: System.Runtime.Serialization.SerializationBinder binder=
new ExamBinder();
Exam soapExamFromBinder =
XmlUtil.SoapToFromFile(txtFilePath.Text,binder) as Exam;
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||