Saturday 30 June 2007

XML Serialization in ASP.NET

XML Serialization in ASP.NET

In the past, maintaining the state of an object in ASP often required some very inventive and painstaking code. In the brave new world of .NET, however, Object Serialization offers us a comparatively easy way to do just that, as well as some other useful tasks.
As a kid, I remember waking up on many a cold morning and stumbling into the kitchen with my eyes half-closed, looking forward to whatever Mom had prepared for breakfast, only to find an anticlimactic bowl of steaming hot just-add-boiling-water instant oatmeal waiting for me on the table. At least I wasn't like the more unfortunate kids whose mothers force-fed them that white silt of death, powdered milk. I am absolutely certain that something must go seriously awry in the dehydration process of milk because upon rehydration, that stuff is just plain nasty.
Be that as it may, I think that at least one of the developers involved in creating the .NET Framework must have been one of those abused children. I see powdered milk fingerprints all over some of the new data management techniques in .NET. Then again, in an age of dehydrated/rehydrated food products, what could be more logical than dehydrated/rehydrated data?
Object Serialization
The technique to which I refer is called "Object Serialization". 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, in order to be stored for some later use. In other words, the object is "dehydrated" and put away until we need to use it again. Let's look at an example to clarify this idea a little further. Suppose we have an object defined and instantiated as shown below:
Public Class Person
private m_sName as string
private m_iAge as integer

public property Name() as string
get
return m_sName
end get
set(byval sNewName as string)
m_sName = sNewName
end set
end property

public property Age() as integer
get
return m_iAge
end get
set(byval iNewAge as integer)
m_iAge = iNewAge
end set
end property
End Class
dim oPerson as New Person()
oPerson.Name = "Powdered Toast Man"
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:

Powdered Toast Man
38

Then, at some later time when we needed to use the object again, we could just deserialize ("rehydrate") it and have our object restored to us just as it was at the moment we serialized it.
Why All This Dried Food?
"This serialization and deserialization is all well and good," you may be saying at this point, "but what can it be used for in real world web applications?" Very good question. What would be the sense of dehydrating a glass of milk just to rehydrate it and then dehydrate it again without drinking any? Never mind that it'll probably taste like the inside of your little brother's sock drawer (maybe I should have come up with a more tasty dehydrated food for this analogy). 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.
The Dehydration Process

"Okay, okay, I get the picture," you may be muttering now, "but how do I do it?" I'm going to walk you through a simple example of serializing an object to be saved to disk as an XML file. Keep in mind that in .NET we can serialize objects into a binary format or SOAP format as well as into XML, but we will focus solely on XML for this article for the sake of brevity. Also, the object in the example is obviously very simplistic and wouldn't be practical in the real world, but it will serve as a means to clearly illustrate how to serialize an object. The same principles used in this example can then be applied to more complicated tasks. Note that all the code used for this example is available for download at the end of this article).
First of all, let's take a look at the class that is the blueprint for our object (this snippet can be found in the file, xmlser.aspx).
_
public class Person
private m_sName as string
private m_iAge as integer
_
public property Name() as string
get
return m_sName
end get
set(byval sNewName as string)
m_sName = sNewName
end set
end property
_
public property Age() as integer
get
return m_iAge
end get
set(byval iNewAge as integer)
m_iAge = iNewAge
end set
end property
public function Hello() as string
dim s as string
s = "Hi! My name is " & Name & " and I am " & Age & " years old."
return s
end function
public function Goodbye() as string
return "So long!"
end function
end class
This looks pretty similar to the class we saw earlier but with a few adjustments. First of all, notice the lines that say,
")
Response.Write("Goodbye() = " & oLucky.Goodbye() & "
")
'Serialize object to XML and write it to XML file
oStmW = new StreamWriter(Server.MapPath("lucky.xml"))
oXS.Serialize(oStmW, oLucky)
oStmW.Close()
First of all, we declare and instantiate an XMLSerializer object. You'll notice that we had to tell it right from the onset, using the GetType() function, what type of object it's going to be serializing. Next you see that we assign values to the Name and Age properties of the Person object we instantiated. Then we output to the ASP.NET page what the properties are set to by calling the Hello() and Goodbye() methods of the Person object.

Remember that this is only so that we can see what's happening with the object during this process. Next comes the good stuff: We instantiate a StreamWriter object and tell it that it will be writing to a file called, lucky.xml. We then call the Serialize() method of the XMLSerializer object and send it our Person object to be serialized as well as the StreamWriter object so it will write the resulting XML to the file specified. Then we close the StreamWriter, thereby closing the file.
That's it. If everything works correctly, an XML file (lucky.xml) will be written to disk. It should look like this:


52
Lucky Day

Notice that the names of the XML elements are exactly as we specified in the and attributes in the class earlier. In Part 2 we'll examine how to "rehydrate" our XML into an object instance.
Just Add Water
Now let's look at this from the opposite angle. We've just seen how to serialize an object into an XML file and save it to disk, but now suppose we already had an XML file saved and wanted to use it to instantiate an object. In the downloadable code found at the end of this article, you will find a file called, ned.xml. We’re going to use that XML file to create a Person object. Its contents look like this:


47
Ned Nederlander

You'll notice that this XML document has exactly the same structure as the XML file that we wrote to disk a moment ago but the data it contains is, of course, different. Now put on your wicked mad scientist grins and look at the code required to bring this beast of an object to life:
dim oNed as Person
dim oStmR as StreamReader
'Pull in contents of an object serialized into an XML file
'and deserialize it into an object
oStmR = new StreamReader(Server.MapPath("ned.xml"))
oNed = oXS.Deserialize(oStmR)
oStmR.Close()
'Display property values
Response.Write("Hello() = " & oNed.Hello() & "
")
Response.Write("Goodbye() = " & oNed.Goodbye() & "
")
Before anything else, we declare a Person object and StreamReader object. Next, we create an instance of the StreamReader object and feed it the stored XML file. Then we instantiate the Person object by calling the Deserialize() method of the XMLSerializer object. This method uses the StreamReader object to read the contents of the XML file and then instantiates an object whose state matches that described in the XML file. Finally, we close up the StreamReader object and then output the results of the newly created object's Hello() and Goodbye() methods just to prove that it was successfully created. It's just like that instant oatmeal Mom used to make.
Note: Something important to remember is that when an object is instantiated through Deserialization, its constructor is not called. Just keep that in mind if you plan on doing this with any objects which are very dependent on their constructors performing some crucial function.
Do I Have To Keep My Raisins?
Perhaps you are wondering now, "Pretty cool - but what if I don't want to save my object to disk?" Another good question. There's no reason you would have to. Let's suppose that for some reason, you needed to serialize an object into an XML string to be used for some purpose and then forgotten or re-instantiated or whatever else. This can be accomplished in almost the same way that was demonstrated earlier. However, instead of using a StreamWriter object in the process, we will use a StringWriter object. See the code snippet below:
dim oDusty as new Person()
dim oStrW as new StringWriter()
dim sXML as string
'Set properties
oDusty.Name = "Dusty Bottoms"
oDusty.Age = 51
'Serialize object into an XML string
oXS.Serialize(oStrW, oDusty)
sXML = oStrW.ToString()
oStrW.Close()
As you can see, we instantiate a new Person object and StringWriter object and then assign values to the Name and Age properties of the Person object. We then call the Serialize() method of the XMLSerializer object and the Person object is serialized into an XML document and placed in the StringWriter object.
Before we move on, it is important to understand some things about the StringWriter and StreamWriter objects and Inheritance. The Serialize() method of the XMLSerializer object is an overloaded method and one of its signatures is: Overloads Public Sub Serialize(TextWriter, Object). This means we must send it a TextWriter object and some other object.
"Wait a minute!" I hear you shouting, "If it needs to be sent a TextWriter object, why are we sending it StringWriters and StreamWriters?" That's because of Inheritance.
In object oriented development, objects can be derived from other objects, inheriting some or all of the original object's characteristics. This is where StringWriter and StreamWriter come from. They are "descendants" of TextWriter. Think of it this way: A man named Fritz Meyer has two children, Hansel and Gretel. Hansel is not Fritz, but he is a Meyer as is Gretel and when they have a Meyer family reunion, Fritz, Hansel, and Gretel can all get in the door because they are all Meyers. Similarly, because StreamWriter and StringWriter are both descended from TextWriter, they can be used with this call to Serialize(). Unfortunately, StreamWriter doesn't have a way to present its contents as a string data type, but StringWriter does and we are interested, at this point, in getting the XML string rather than saving it to a file.
That is why, in the code snippet above, we send a StringWriter to Serialize() instead of a StreamWriter. (For more information on inheritence and how it is used in .NET, be sure to read: Using Object-Orientation in ASP.NET: Inheritance.)
After the serialization takes place, we capture the XML string by calling the ToString() method of the StringWriter object and placing the results in a string variable. Next, we close the StringWriter object because we no longer need it. We now have our hands on the XML string and can do with it what we please. In the downloadable example code, all we do with it is output it to the browser.

Conclusion
As you have just seen, serialization is fairly easy to implement. I've already listed several possible reasons to use serialization in your applications and now that you know how to do it, I will leave the rest to your own capable imaginations. This article has focused only on how to serialize an object into an XML document, but please remember that objects can also be serialized into binary or SOAP formats. To learn more about those types of serialization, look up the BinaryFormatter class and the SOAPFormatter class.
Maybe this powerful technology didn't really have its humble beginnings in the bottom of a glass of powdered milk - but for some reason, it makes me smile to think so. Then again, maybe someday we'll see Bill Gates or one of his .NET guys sporting a liquidy white moustache on one of those "Got Milk" ads.


No comments: