Soap Serialization
Soap serialization works similarly to binary serialization. First, you need to add a reference to the System.Runtime.Serialization.Formatters.Soap.dll assembly. Then you add an Imports System.Runtime.Serialization.Formatters.Soap
directive. At this point you can serialize and deserialize your
objects. To continue the example of the typed collection shown in the
previous section, write the following code to accomplish serialization
with the Soap formatter:
'Requires an Imports System.Runtime.Serialization.Formatters.Soap directive
Dim stringToSerialize As String = "Serialization demo with VB"
Dim targetFile As New FileStream("C:\temp\SerializedData.xml",
FileMode.Create)
Dim formatter As New SoapFormatter
formatter.Serialize(targetFile, stringToSerialize)
targetFile.Close()
formatter = Nothing
Basically there is no difference in the syntax for the Soap formatter if compared to the binary one.
The SoapFormatter
class does not allow serializing generic collections. This is the reason
why a simpler example against a single string is provided.
|
You can still examine the result of the serialization process with the Windows Notepad. Figure 2 shows how the target file stores information in a XML fashion.
Typically
the Soap serialization is intended to be used when working with Soap
web services.
Providing Serialization for Custom Objects
You can make your custom
objects serializable so that you can apply the previously described
techniques for persisting and re-creating objects’ state. To be
serializable, a class (or structure) must be decorated with the Serializable attribute. This is the most basic scenario and is represented by the following implementation of the Person class:
Imports System.Runtime.Serialization
<Serializable()>
Public Class Person
Public Property FirstName As String
Public Property LastName As String
Public Property Age As Integer
Public Property Address As String
End Class
If you do not need to get
control over the serialization process, this is all you need. By the
way, there can be certain situations that you need to handle. For
instance, you might want to disable serialization for a member that
could result obsolete if too much time is taken between serialization
and deserialization. Continuing the Person class example, we decide to disable serialization for the Age
member because between serialization and deserialization the
represented person might be older than the moment when serialization
occurred. To accomplish this you apply the NonSerialized
attribute. The big problem here is that this is a field-level
attribute; therefore, it cannot be applied to properties. In such
situations using auto-implemented properties is not possible; therefore,
you must write them the old-fashioned way. The following code shows how
you can prevent the Age member from being serialized:
<NonSerialized()> Private _age As Integer
Public Property Age As Integer
Get
Return _age
End Get
Set(ByVal value As Integer)
_age = value
End Set
End Property
The subsequent problem is that
you need a way for assigning a valid value to nonserialized members when
deserialization occurs. The most common technique is implementing the IDeserializationCallBack interface that exposes an OnDeserialization method where you can place your initialization code. The following is the revisited code for the Person class according to the last edits:
Imports System.Runtime.Serialization
<Serializable()>
Public Class Person
Implements IDeserializationCallback
Public Property FirstName As String
Public Property LastName As String
<NonSerialized()> Private _age As Integer
Public Property Age As Integer
Get
Return _age
End Get
Set(ByVal value As Integer)
_age = value
End Set
End Property
Public Sub OnDeserialization(ByVal sender As Object) Implements _
System.Runtime.Serialization.IDeserializationCallback.
OnDeserialization
'Specify the new age
Me.Age = 32
End Sub
End Class
When the deserialization process invokes the OnDeserialization
method, members that were not serialized can be correctly initialized
anyway. Another consideration that you need to take care of is
versioning. When you upgrade your application to a new version, you
might also want to apply some changes to your classes, for example
adding new members. This is fine but can result in problems if the
previous version of your application attempts to deserialize an object
produced by the new version. To solve this problem, you can mark a
member as OptionalField. In this way the deserialization process is not affected by new members and both BinaryFormatter and SoapFormatter will not throw exceptions if they encounter new members during the process. Because the OptionalField
attribute works at field level, this is another situation in which you
cannot take advantage of auto-implemented properties. The following code
shows how you can mark the Address member in the Person class as optional:
<OptionalField()> Private _address As String
Public Property Address As String
Get
Return _address
End Get
Set(ByVal value As String)
_address = value
End Set
End Property
The
member is still involved in the serialization process, but if a
previous version of the application attempts to perform deserialization,
it will not throw exceptions when it encounters this new member that
was not expected.
NonSerialized events
Visual Basic 2010 introduces a new feature known as NonSerialized Events. Basically you can now decorate an event with the NonSerialized
attribute in custom serialization. A common scenario for applying this
technique is when you work on classes that implement the INotifyPropertyChanged
interface because it is more important serializing data and not an
event that just notifies the user interface of changes on data. The
following code shows an example about NonSerialized events inside a class that implements INotifyPropertyChanged:
<Serializable()>
Public Class Customer
Implements INotifyPropertyChanged
<NonSerialized()>
Public Event PropertyChanged(
ByVal sender As Object,
ByVal e As System.ComponentModel.PropertyChangedEventArgs) _
Implements System.ComponentModel.
INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal strPropertyName As String)
If Me.PropertyChangedEvent IsNot Nothing Then
RaiseEvent PropertyChanged(Me,
New PropertyChangedEventArgs(strPropertyName))
End If
End Sub