Monday, May 25, 2020

All About Serializing in Visual Basic

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. Thats why serialization is such a key process in .NET and Visual Basic. Below is a section on custom serialization by implementing the ISerializable interface and coding a New and a GetObjectData subroutine. As a first example of serialization, lets 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 SerializeParmsSerializable() Public Class ParmExample  Ã‚  Ã‚  Public Parm1Name As String Parm1 Name  Ã‚  Ã‚  Public Parm1Value As Integer 12345  Ã‚  Ã‚  Public Parm2Name As String  Ã‚  Ã‚  Public Parm2Value As DecimalEnd ClassEnd Module Then, individual values can be saved to a file like this: Imports System.Runtime.Serialization.Formatters.BinaryImports System.IOPublic 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 SubEnd Class And those same values can be retrieved like this: Imports System.Runtime.Serialization.Formatters.BinaryImports System.IOPublic 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 SubEnd 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 theyre used for a lot of different things.  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 dont 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. Well 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, theyll 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 isnt readable text, but you should be able to see any strings in your serialized file. Well 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 isnt as fast and cant capture some object information, but its 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 dont 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, were going to use one of those extensions of XML, a technology called SOAP. This used to mean Simple Object Access Protocol but now its just a name. (SOAP has been upgraded so much that the original name doesnt 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.SoapDim f As New SoapFormatter This time, if you check out the same ParmData file in Notepad, youll see that the whole thing is in readable XML text such as ... Parm1Name idref-3Parm1 Name/Parm1NameParm1Value12345/Parm1ValueParm2Name idref-4Parm2 Name/Parm2NameParm2Value54321.12345/Parm2Value There is also a lot of additional XML there thats 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 its 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 isnt a complete example. The purpose is to show you the new code that is needed for custom serialization. Imports System.Runtime.SerializationSerializable() _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 SubEnd 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 youre implementing an interface. The class will normally have formal properties and methods coded as well ... Generic PropertyPrivate newPropertyValue As StringPublic Property NewProperty() As String  Ã‚  Ã‚  Get  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  Return newPropertyValue  Ã‚  Ã‚  End Get  Ã‚  Ã‚  Set(ByVal value As String)  Ã‚  Ã‚  Ã‚  Ã‚  Ã‚  newPropertyValue value  Ã‚  Ã‚  End SetEnd Property Generic MethodPublic Sub MyMethod()  Ã‚  Ã‚  method codeEnd 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 Ive found in writing this article dont seem to have actual working code. One wonders whether the author actually executed any code before writing the article sometimes.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.