All About Serializing in Visual Basic

What you need to know about it in one place!

Young woman working at desk in office
Jamie Grill/Getty Images

Serialization is the process of converting an object into a linear sequence of bytes called a "byte stream." Deserialization just reverses the process. But why would you want to convert an object into a byte stream?

The main reason is so you can move the object around. Consider the possibilities. Since "everything is an object" in .NET, you can serialize anything and save it to a file. So you could serialize pictures, data files, the current state of a program module ('state' is like a snapshot of your program at a point in time so you could temporarily suspend execution and start again later) ...

whatever you need to do.

You can also store these objects on disk in files, send them over the web, pass them to a different program, keep a backup copy for safety or security. The possibilities are quite literally endless.

That's why serialization is such a key process in .NET and Visual Basic. I've written about it before, but in this article, I've added a section on custom serialization by implementing the ISerializable interface and coding a New and a GetObjectData subroutine.

As a first example of serialization, let's do one of the easiest programs, but also one of the most useful: serializing data, and then deserializing data in simple class to and from a file. In this example, the data is not only serialized, but the structure of the data is saved too. The structure here is declared in a module to keep things ... well ... structured.

Module SerializeParms
<Serializable()> Public Class ParmExample
   Public Parm1Name As String = "Parm1 Name"
   Public Parm1Value As Integer = 12345
   Public Parm2Name As String
   Public Parm2Value As Decimal
End Class
End Module

Then, individual values can be saved to a file like this:

Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO
Public Class Form1
   Private Sub mySerialize_Click( _
      ByVal sender As System.Object, _
      ByVal e As System.EventArgs) _
      Handles mySerialize.Click
      Dim ParmData As New ParmExample
      ParmData.Parm2Name = "Parm2 Name"
      ParmData.Parm2Value = 54321.12345
      Dim s As New FileStream("ParmInfo", FileMode.Create)
      Dim f As New BinaryFormatter
      f.Serialize(s, ParmData)
      s.Close()
   End Sub
End Class

And those same values can be retrieved like this:

Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO
Public Class Form1
   Private Sub myDeserialize_Click( _
      ByVal sender As System.Object, _
      ByVal e As System.EventArgs) _
      Handles myDeserialize.Click
      Dim s = New FileStream("ParmInfo", FileMode.Open)
      Dim f As New BinaryFormatter
      Dim RestoredParms As New ParmExample
      RestoredParms = f.Deserialize(s)
      s.Close()
      Console.WriteLine(RestoredParms.Parm1Name)
      Console.WriteLine(RestoredParms.Parm1Value)
      Console.WriteLine(RestoredParms.Parm2Name)
      Console.WriteLine(RestoredParms.Parm2Value)
   End Sub
End Class

A Structure or a collection (such as an ArrayList) rather than a Class could also be serialized to a file this same way.

Now that we have gone over the basic serializing process, lets look at the specific details that are part of the process on the next page.

One of the first things you should notice about this example is the <Serializable()> attribute in the Class. Attributes are just more information that you can provide to VB.NET about an object and they're used for a lot of different things. For an in depth explanation of attributes, try my four part article about attributes in VB.NET. Read the article here. The attribute in this code tells VB.NET to add extra code so that later on, everything in this class can be serialized.

If there are specific items in the Class that you don't want to be serialized, you can use the <NonSerialized()> attribute to exclude them:

<NonSerialized()> Public Parm3Value As String = "Whatever"

In the example, notice is that Serialize and Deserialize are methods of the BinaryFormatter object (f in this example).

f.Serialize(s, ParmData)

This object takes the FileStream object and the object to be serialized as parameters. We'll see that VB.NET offers another object that allows the result to be expressed as XML.

And one final note, if your object includes other subordinate objects, they'll be serialized too! But since all objects that are serialized must be marked with the <Serializable()> attribute, all of these child objects must be marked that way too.

Just to be completely clear about what is happening in your program, you might want to display the file named ParmData in Notepad to see what serialized data looks like.

(If you followed this code, it should be in the bin.Debug folder in your project.) Since this is a binary file, most of the content isn't readable text, but you should be able to see any strings in your serialized file. We'll do an XML version next and you might want to compare the two just to be aware of the difference.

Serializing to XML instead of a binary file requires very few changes. XML isn't as fast and can't capture some object information, but it's far more flexible. XML can be used by just about any other software technology in the world today. If you want to be sure your file structures don't "tie you into" Microsoft, this is a good option to look into. Microsoft is emphasizing "LINQ to XML" to create XML data files in their latest technology but many people still prefer this method.

The 'X' in XML stands for eXtensible. In our XML example, we're going to use one of those extensions of XML, a technology called SOAP. This used to mean "Simple Object Access Protocol" but now it's just a name. (SOAP has been upgraded so much that the original name doesn't fit that well anymore.)

The main thing that we have to change in our subroutines is the declation of the serialization formatter. This has to be changed in both the subroutine that serializes the object and the one that deserializes it again. For the default configuration, this involves three changes to your program. First, you have to add a Reference to the project. Right-click the project and select Add Reference .... Make sure ...

System.Runtime.Serialization.Formatters.Soap

... has been added to the project.

Then change the two statements in the program that references it.

Imports System.Runtime.Serialization.Formatters.Soap

Dim f As New SoapFormatter

This time, if you check out the same ParmData file in Notepad, you'll see that the whole thing is in readable XML text such as ...

<Parm1Name id="ref-3">Parm1 Name</Parm1Name>
<Parm1Value>12345</Parm1Value>
<Parm2Name id="ref-4">Parm2 Name</Parm2Name>
<Parm2Value>54321.12345</Parm2Value>

There is also a lot of additional XML there that's necessary for the SOAP standard in the file as well. If you want to verify what the <NonSerialized()> attribute does, you can add a variable with that attribute and look at the file to verify that it's not included.

The example we just coded only serialized the data, but suppose you need to control how the data is serialized. VB.NET can do that too!

To accomplish this, you need to get a little deeper into the concept of serialization. VB.NET has a new object to help out here: SerializationInfo. Although you have the ability to code custom serialization behavior, it comes with a cost of extra coding.

The basic extra code is shown below.

Remember, this class is used instead of the ParmExample class shown in the earlier example. This isn't a complete example. The purpose is to show you the new code that is needed for custom serialization.

Imports System.Runtime.Serialization
<Serializable()> _
Public Class CustomSerialization
   Implements ISerializable
   ' data to be serialized here
   ' Public SerializedVariable as Type
   Public Sub New()
   ' default constructor when the class
   ' is created - custom code can be
   ' added here too
   End Sub
   Public Sub New( _
      ByVal info As SerializationInfo, _
      ByVal context As StreamingContext)
      ' initialize your program variables from
      ' a serialized data store
   End Sub
   Public Sub GetObjectData( _
      ByVal info As SerializationInfo, _
      ByVal context As StreamingContext) _
      Implements ISerializable.GetObjectData
      ' update the serialized data store
      ' from program variables
   End Sub
End Class

The idea is that now you can (and, in fact, you must) do all of the updating and reading of data in the serialized data store in the New and GetObjectData subroutines. You must also include a generic New constructor (no parameter list) because you're implementing an interface.

The class will normally have formal properties and methods coded as well ...

' Generic Property
Private newPropertyValue As String
Public Property NewProperty() As String
   Get
      Return newPropertyValue
   End Get
   Set(ByVal value As String)
      newPropertyValue = value
   End Set
End Property

' Generic Method
Public Sub MyMethod()
   'method code
End Sub

The resulting serialized class can create unique values in the file based on the code you supply. For example, a real-estate class might update a the value and address of a house but the class would serialize a calculated market classification as well.

The New subroutine will look something like this:

Public Sub New( _
   ByVal info As SerializationInfo, _
   ByVal context As StreamingContext)
   ' initialize your program variables from
   ' a serialized data store
   Parm1Name = info.GetString("a")
   Parm1Value = info.GetInt32("b")
   ' New sub continues ...

When Deserialize is called on a BinaryFormatter object, this sub is executed and a SerializationInfo object is passed to the New subroutine. New can then do whatever is necessary with the serialized data values. For example ...

MsgBox("This is Parm1Value Times Pi: " _
   & (Parm1Value * Math.PI).ToString)

The reverse happens when Serialize is called, but the BinaryFormatter object calls GetObjectData instead.

Public Sub GetObjectData( _
   ByVal info As SerializationInfo, _
   ByVal context As StreamingContext) _
   Implements ISerializable.GetObjectData
   ' update the serialized data store
   ' from program variables
   If Parm2Name = "Test" Then
      info.AddValue("a", "This is a test.")
   Else
      info.AddValue("a", "No test this time.")
   End If
   info.AddValue("b", 2)

Notice that the data is added to the serialized file as name/value pairs.

A lot of the web pages I've found in writing this article don't seem to have actual working code. One wonders whether the author actually executed any code before writing the article sometimes. All of the code use here can be downloaded at this link!