[RESOLVED] [2005] Problem Serializing
EDIT: Found the solution to this problem. Solution begins in post #13 below.
I have a serializable class Person
The Person class used to be contained in a project along with a Form that I was using for testing.
I was using the class with no problems while the Person class was a part of the same project as the form I was using for testing. Serializing and Deserializing an object of type Person was successful.
Then I moved the Person class to a class library.
The Class works fine from the test project except it won't serialize.
I just have some buttons on the form that create a Person and then Serialize and Deserialize the Person.
Now however, when I try to serialize a Person object I get an error message that the form used in the testing project isn't serializable; i.e. the form itself is not serializable.
What might be the problem?
EDIT: Found the solution to this problem. Solution begins in post #13 below.
Re: [2005] Problem Serializing
Here is the error message:
Type 'TestSerializeAClass.Form1' in Assembly 'TestSerializeAClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
Re: [2005] Problem Serializing
You have to apply the Serializable attribute to the class.
Re: [2005] Problem Serializing
That sounds suss... you're serialising a Person object, but you get that error about the form?
Show us the code that you use to serialise the Person object.
Re: [2005] Problem Serializing
Hmmm... should have read more carefully.
Re: [2005] Problem Serializing
Ok, I've narrowed it down to the fact that I was trying to serialize a delegate and the associated event declaration.
So while the error message I copied to post #2 seems a little strange, the source has been identified.
I apparently need to learn how to properly serialize a delegate.
So, I'll be reading on how to handle the problem.
In the mean time, If anyone can point me in the right direction please let me know.
Re: [2005] Problem Serializing
Here's the client class (Form) code:
Anyone see a problem with it that would cause the following error?
Type 'TestSerializeAClass.Form1' in Assembly 'TestSerializeAClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
Code:
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO
Imports DILU.Utilities
Public Class Form1
#Region "Serialize"
Private Sub Serialize(ByVal fileName As String, ByVal person As Person)
Dim fs As FileStream = New FileStream(fileName, FileMode.OpenOrCreate)
Dim bf As New BinaryFormatter
Try
bf.Serialize(fs, person)
Catch exception As SerializationException
Throw
Finally
fs.Close()
End Try
End Sub
#End Region
#Region "Deserialize"
Private Function Deserialize(ByVal fileName As String) As Person
Dim fs As FileStream = New FileStream(fileName, FileMode.Open)
Dim p As New Person
Try
Dim formatter As New BinaryFormatter
p = DirectCast(formatter.Deserialize(fs), Person)
Catch exception As Runtime.Serialization.SerializationException
Throw
Finally
fs.Close()
End Try
Return p
End Function
#End Region
#Region "Button One Click"
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim p As New Person("FirstName", "LastName")
AddHandler p.LastNameChanged, AddressOf PersonsLastNameChanged
AddHandler p.FirstNameChanged, AddressOf PersonsFirstNameChanged
Me.TextBox1.Text = p.FirstName
Me.TextBox2.Text = p.LastName
Serialize("C:\TestBinaryFormatterSerialization.txt", p)
Dim np As Person = Deserialize("C:\TestBinaryFormatterSerialization.txt")
Me.TextBox3.Text = np.ToString
Me.TextBox4.Text = np.LastName
End Sub
#End Region
#Region "Event Handlers For Names Changed"
Private Sub PersonsLastNameChanged(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
MessageBox.Show("Last Name Changed, New Last Name Is: " & e.NewLastName)
End Sub
Private Sub PersonsFirstNameChanged(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
MessageBox.Show("First Name Changed, New First Name Is: " & e.NewFirstName)
End Sub
#End Region
End Class
Here Is The Code From The Class Library:
First Name Changed Event Args Class:
Code:
Namespace Utilities
<Serializable()> _
Public Class FirstNameChangedEventArgs
Inherits EventArgs
Sub New(ByVal oldFirstName As String, ByVal newFirstName As String, ByVal sendingPerson As Person)
mOldFirstName = oldFirstName
mNewFirstName = newFirstName
End Sub
Private mOldFirstName As String
Public ReadOnly Property OldFirstName() As String
Get
Return mOldFirstName
End Get
End Property
Private mNewFirstName As String
Public ReadOnly Property NewFirstName() As String
Get
Return mNewFirstName
End Get
End Property
Private mPerson As Person
Public ReadOnly Property Person() As Person
Get
Return mPerson
End Get
End Property
End Class
End Namespace
Last Name Changed Event Args Class:
Code:
Namespace Utilities
<Serializable()> _
Public Class LastNameChangedEventArgs
Inherits EventArgs
Sub New(ByVal oldLastName As String, ByVal newLastName As String, ByVal sendingPerson As Person)
mOldLastName = oldLastName
mNewLastName = newLastName
End Sub
Private mOldLastName As String
Public ReadOnly Property OldLastName() As String
Get
Return mOldLastName
End Get
End Property
Private mNewLastName As String
Public ReadOnly Property NewLastName() As String
Get
Return mNewLastName
End Get
End Property
Private mPerson As Person
Public ReadOnly Property Person() As Person
Get
Return mPerson
End Get
End Property
End Class
End Namespace
The Person Class:
Code:
Namespace Utilities
<Serializable()> _
Public Class Person
#Region "Event And Delegate Declarations"
<Serializable()> _
Public Delegate Sub LastNameChangedEventHandler(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
Public Event LastNameChanged As LastNameChangedEventHandler
<Serializable()> _
Public Delegate Sub FirstNameChangedEventHandler(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
Public Event FirstNameChanged As FirstNameChangedEventHandler
#End Region
#Region "Private and Public Fields Not Wrapped By Properties (NONE)"
'None
#End Region
#Region "Constructors"
Sub New()
Me.New("Unknown", "Unknown")
End Sub
Sub New(ByVal firstName As String, ByVal lastName As String)
If Not (String.IsNullOrEmpty(firstName)) Then
mFirstName = firstName
Else
mFirstName = "Unknown"
End If
If Not (String.IsNullOrEmpty(lastName)) Then
mLastName = lastName
Else
mLastName = "Unknown"
End If
End Sub
#End Region
#Region "Instance Public Properties and Their Wrapped Fields"
Private mLastName As String
Public Property LastName() As String
Get
Return mLastName
End Get
Set(ByVal value As String)
If Not mLastName = value Then
Dim Args As New LastNameChangedEventArgs(mLastName, value, Me)
OnLastNameChanged(Args)
mLastName = value
End If
End Set
End Property
Private mFirstName As String
Public Property FirstName() As String
Get
Return mFirstName
End Get
Set(ByVal value As String)
If Not mFirstName = value Then
Dim args As New FirstNameChangedEventArgs(mFirstName, value, Me)
mFirstName = value
End If
End Set
End Property
#End Region
#Region "Methods"
Public Overrides Function ToString() As String
Return String.Concat(FirstName, " ", LastName)
End Function
Protected Overridable Sub OnLastNameChanged(ByVal e As LastNameChangedEventArgs)
RaiseEvent LastNameChanged(Me, e)
End Sub
Protected Overridable Sub OnFirstNameChanged(ByVal e As FirstNameChangedEventArgs)
RaiseEvent FirstNameChanged(Me, e)
End Sub
#End Region
#Region "Static Public Methods and Properties (NONE)"
'NONE
#End Region
#Region "Methods In Interfaces (NONE)"
'NONE
#End Region
#Region "Private Helper Methods (NONE)"
'NONE
#End Region
End Class
End Namespace
Re: [2005] Problem Serializing
Ok, the reason that the message "Type 'TestSerializeAClass.Form1' in Assembly 'TestSerializeAClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable" occurs is because the Form is being included in the object graph of the Person class being serialized.
This is because the event delegate points to the Form class which is not serializeable (Forms never are).
However, when I try to mark the event with the NonSerialized attribute I get this message:
"Attribute 'NonSerializedAttribute' cannot be applied to 'LastNameChanged' because the attribute is not valid on this declaration type"
Here is what I tried:
Code:
#Region "Event And Delegate Declarations"
<Serializable()> _
Public Delegate Sub LastNameChangedEventHandler(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
<NonSerialized()> _ 'Error Here
Public Event LastNameChanged As LastNameChangedEventHandler
<Serializable()> _
Public Delegate Sub FirstNameChangedEventHandler(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
Public Event FirstNameChanged As FirstNameChangedEventHandler
#End Region
So how do I correctly mark the declaration for the event so that it will not be serialized?
Re: [2005] Problem Serializing
FourBlades, Have you come up with a solution yet?
I read that the NonSerialized attribute can be used in C# but not in VB.
Might I suggest that prior to Serializing you remove any handlers from the using Form that registers to recieve event notifications from the Person class? That way the delegates within the Person class will not reference the Form and the Form will therefore not be included in the serialization object graph.
Just a suggestion.
Re: [2005] Problem Serializing
Thank you for the idea. However, I've already thought of that.
I'm hoping someone else might come up with a more elegant solution that circumvents having to deregister all of the event handlers prior to serialization.
Re: [2005] Problem Serializing
Ok, well let me know if you come up with an acceptable solution.
Re: [2005] Problem Serializing
Re: [2005] Problem Serializing
Solution To Problem:
The problem was as follows:
When you serialize an object (I'm using the BinaryFormatter) an object graph is created that includes any object pointed to by the primary object being serialized. If the primary object being serialized contains a delegate (as in maybe an event delegate) andalso another object has subscribed to receive an event notification from the primary object (the one being serialized) then the subscribing object is also included in the object graph. The runtime attempts to serialize the subscribing object. If the subscribing object is not serializable, an error occurs.
The solution to this problem is to exclude the delegates from being serialized.
To exclude the delegates from being serialized you must handle custom serialization.
Custom serialization requires the class author to implement the ISerializable interface.
Within the implementation of the ISerializable interface you must write code to exclude any class members of type Delegate or any type that derives from Delegate.
The complete Person class that implements the ISerializable interface is shown below:
Code:
Imports System
Imports System.Security.Permissions
Imports System.Runtime.Serialization
Namespace Utilities
<Serializable()> _
Public Class Person
Implements ISerializable
#Region "Event And Delegate Declarations"
'<Serializable()> _
Public Delegate Sub LastNameChangedEventHandler(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
Public Event LastNameChanged As LastNameChangedEventHandler
'<Serializable()> _
Public Delegate Sub FirstNameChangedEventHandler(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
Public Event FirstNameChanged As FirstNameChangedEventHandler
#End Region
#Region "Private and Public Fields Not Wrapped By Properties (NONE)"
'None
#End Region
#Region "Constructors"
Sub New()
Me.New("Unknown", "Unknown")
End Sub
Sub New(ByVal firstName As String, ByVal lastName As String)
If (String.IsNullOrEmpty(firstName)) Then
Throw New ArgumentNullException("firstName")
Else
mFirstName = firstName
End If
If (String.IsNullOrEmpty(lastName)) Then
Throw New ArgumentNullException("lastName")
Else
mLastName = lastName
End If
End Sub
'The CLR calls this special constructor when this (Me) object is deserialized
'Should have Protected scope if other classes can be inherited from this class
'Otherwise, if this class is sealed (cannot be inherited from) the scope can be Private
Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
SerializationHelpers.ISerializableContructorHelper(info, context, Me)
End Sub
<SecurityPermissionAttribute(SecurityAction.Demand, Flags:=SecurityPermissionFlag.SerializationFormatter)> _
Protected Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) _
Implements ISerializable.GetObjectData
SerializationHelpers.GetObjectDataHelper(info, context, Me)
End Sub
#End Region
#Region "Instance Public Properties and Their Wrapped Fields"
Private mLastName As String
Public Property LastName() As String
Get
Return mLastName
End Get
Set(ByVal value As String)
If Not mLastName = value Then
Dim Args As New LastNameChangedEventArgs(mLastName, value, Me)
Me.OnLastNameChanged(Args)
mLastName = value
End If
End Set
End Property
Private mFirstName As String
Public Property FirstName() As String
Get
Return mFirstName
End Get
Set(ByVal value As String)
If Not mFirstName = value Then
Dim args As New FirstNameChangedEventArgs(mFirstName, value, Me)
Me.OnFirstNameChanged(args)
mFirstName = value
End If
End Set
End Property
#End Region
#Region "Instance Public Methods"
Public Overrides Function ToString() As String
Return String.Concat(FirstName, " ", LastName)
End Function
Protected Overridable Sub OnLastNameChanged(ByVal e As LastNameChangedEventArgs)
RaiseEvent LastNameChanged(Me, e)
End Sub
Protected Overridable Sub OnFirstNameChanged(ByVal e As FirstNameChangedEventArgs)
RaiseEvent FirstNameChanged(Me, e)
End Sub
#End Region
#Region "Static Public Methods and Properties (NONE)"
'NONE
#End Region
#Region "Methods In Interfaces (NONE)"
'NONE
#End Region
#Region "Private Helper Methods (NONE)"
'NONE
#End Region
End Class
End Namespace
Re: [2005] Problem Serializing
Opps clicked on Submit Reply instead of Preview Post...more in next post
Re: [2005] Problem Serializing
You'll notice a Protected Sub New and a Protected Overridable Sub GetObjectData highlighted in bold text within the Person class listed two posts above.
These are the two additional code blocks required by the ISerializable interface.
You'll also notice that in those blocks reference is made to a helper class as found below:
Credit for the code goes to the authors of Practical Guidelines And Best Practices For Microsoft Visual Basic and Visual C# Developers a book I would highly recommend to anyone.
Code:
Imports System.Runtime.Serialization
Imports System.Reflection
Namespace Utilities
Public Class SerializationHelpers
''' <summary>
''' Helper routine for serializing objects.
''' (Place a call to this sub into the Serializable Classes Protected Overridable Sub GetObjectData)
''' i.e. place into: Protected Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext)
''' </summary>
''' <param name="info"></param>
''' <param name="context"></param>
''' <param name="obj"></param>
''' <remarks></remarks>
Public Shared Sub GetObjectDataHelper(ByVal info As SerializationInfo, ByVal context As StreamingContext, ByVal obj As Object)
'Get list of serializable members
Dim members() As MemberInfo = GetFilteredSerializableMembers(obj.GetType)
'Read the value of each member
Dim values() As Object = FormatterServices.GetObjectData(obj, members)
'Store in the SerializationInfo Object, using member name
For i As Integer = 0 To members.Length - 1
info.AddValue(members(i).Name, values(i))
Next
End Sub
''' <summary>
''' Helper routine for deserializing objects.
''' Place a call to this function into the Serializable Constructor;
''' i.e. place into: Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext))
''' </summary>
''' <param name="info"></param>
''' <param name="context"></param>
''' <param name="obj"></param>
''' <remarks></remarks>
Public Shared Sub ISerializableContructorHelper(ByVal info As SerializationInfo, ByVal context As StreamingContext, ByVal obj As Object)
'Get the list of serializable members for this object
'excluding members that are of type delegate (filtered)
'Note that members()(below) contains all types of members from the class passed into this sub via obj (byval obj as Object)
Dim members() As MemberInfo = GetFilteredSerializableMembers(obj.GetType)
Dim values(members.Length - 1) As Object
'Read the value for each member
For i As Integer = 0 To members.Length - 1
'retrieve the members from members() that are of type field
'and then get all field's values
Dim member As MemberInfo = members(i)
If member.MemberType = MemberTypes.Field Then
Dim membertype As Type = CType(member, FieldInfo).FieldType
values(i) = info.GetValue(member.Name, membertype)
End If
Next
'assign all serializable members in one operation
FormatterServices.PopulateObjectMembers(obj, members, values)
End Sub
''' <summary>
''' Return an array of serializable MemberInfo for a given type.
''' (Same as FormatterServices.GetSerializableMember, but FILTERS OUT delegate fields.)
''' </summary>
''' <param name="ty"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Shared Function GetFilteredSerializableMembers(ByVal ty As Type) As MemberInfo()
Dim al As New ArrayList
'Get the list of serializable members
For Each mi As MemberInfo In FormatterServices.GetSerializableMembers(ty)
'Add the mi object to the arraylist if it is NOT:
'(a FieldInfo object) [that IS also (AndAlso)] (found in the inheritance hierarchy of the Delegate class)
If Not (TypeOf mi Is FieldInfo AndAlso GetType([Delegate]).IsAssignableFrom(DirectCast(mi, FieldInfo).FieldType)) Then
al.Add(mi)
End If
Next
Return DirectCast(al.ToArray(GetType(MemberInfo)), MemberInfo())
End Function
End Class
End Namespace
You'll have to read up on the ISerializable interface and the purpose of it's members because a complete explanation would be lengthy.
Upon reading up on how these two code blocks work in general; i.e. the purpose for the special sub New and the GetObjectData method you'll quickly see the importance of these helper methods.
A complete example including the using class, the simple Person class, and the Helper class follows In the next post (was not accepted to this post because of it's size):
Re: [2005] Problem Serializing
Using Class example:
Code:
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO
Imports DILU.Utilities
Public Class Form1
#Region "Serialize"
Private Sub Serialize(ByVal fileName As String, ByVal person As Person)
Dim fs As FileStream = New FileStream(fileName, FileMode.OpenOrCreate)
Dim bf As New BinaryFormatter
Try
bf.Serialize(fs, person)
Catch exception As SerializationException
MessageBox.Show("Failed to serialize. Reason: " & exception.Message)
Throw
Finally
fs.Close()
End Try
End Sub
#End Region
#Region "Deserialize"
Private Function Deserialize(ByVal fileName As String) As Person
Dim fs As FileStream = New FileStream(fileName, FileMode.Open)
Dim p As New Person
Try
Dim formatter As New BinaryFormatter
p = DirectCast(formatter.Deserialize(fs), Person)
Catch exception As Runtime.Serialization.SerializationException
Console.WriteLine("Failed to deserialize. Reason: " & exception.Message)
Throw
Finally
fs.Close()
End Try
Return p
End Function
#End Region
#Region "Button One Click"
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim p As Person
Try
p = New Person(" ", " ")
Catch ex As ArgumentNullException
p = Nothing
p = New Person
End Try
Me.TextBox1.Text = p.FirstName
Me.TextBox2.Text = p.LastName
'yes, I actually do have a need for these two events :)
AddHandler p.LastNameChanged, AddressOf PersonsLastNameChanged
AddHandler p.FirstNameChanged, AddressOf PersonsFirstNameChanged
Serialize("C:\BinaryNoDelegateSerialization.txt", p)
Dim np As Person = Deserialize("C:\BinaryNoDelegateSerialization.txt")
Me.TextBox3.Text = np.FirstName
Me.TextBox4.Text = np.LastName
Application.DoEvents()
Threading.Thread.Sleep(3000)
p.LastName = "New Last Name"
p.FirstName = "New First Name"
End Sub
#End Region
#Region "Event Handlers For Names Changed"
Private Sub PersonsLastNameChanged(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
Me.TextBox2.Text = e.NewLastName
End Sub
Private Sub PersonsFirstNameChanged(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
Me.TextBox1.Text = e.NewFirstName
End Sub
#End Region
End Class
Person Class:
Code:
Imports System
Imports System.Security.Permissions
Imports System.Runtime.Serialization
Namespace Utilities
<Serializable()> _
Public Class Person
Implements ISerializable
#Region "Event And Delegate Declarations"
'<Serializable()> _
Public Delegate Sub LastNameChangedEventHandler(ByVal sender As Object, ByVal e As LastNameChangedEventArgs)
Public Event LastNameChanged As LastNameChangedEventHandler
'<Serializable()> _
Public Delegate Sub FirstNameChangedEventHandler(ByVal sender As Object, ByVal e As FirstNameChangedEventArgs)
Public Event FirstNameChanged As FirstNameChangedEventHandler
#End Region
#Region "Private and Public Fields Not Wrapped By Properties (NONE)"
'None
#End Region
#Region "Constructors"
Sub New()
Me.New("Unknown", "Unknown")
End Sub
Sub New(ByVal firstName As String, ByVal lastName As String)
If (String.IsNullOrEmpty(firstName)) Then
Throw New ArgumentNullException("firstName")
Else
mFirstName = firstName
End If
If (String.IsNullOrEmpty(lastName)) Then
Throw New ArgumentNullException("lastName")
Else
mLastName = lastName
End If
End Sub
'The CLR calls this special constructor when this (Me) object is deserialized
'Should have Protected scope if other classes can be inherited from this class
'Otherwise, if this class is sealed (cannot be inherited from) the scope can be Private
Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
SerializationHelpers.ISerializableContructorHelper(info, context, Me)
End Sub
<SecurityPermissionAttribute(SecurityAction.Demand, Flags:=SecurityPermissionFlag.SerializationFormatter)> _
Protected Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) _
Implements ISerializable.GetObjectData
SerializationHelpers.GetObjectDataHelper(info, context, Me)
End Sub
#End Region
#Region "Instance Public Properties and Their Wrapped Fields"
Private mLastName As String
Public Property LastName() As String
Get
Return mLastName
End Get
Set(ByVal value As String)
If Not mLastName = value Then
Dim Args As New LastNameChangedEventArgs(mLastName, value, Me)
Me.OnLastNameChanged(Args)
mLastName = value
End If
End Set
End Property
Private mFirstName As String
Public Property FirstName() As String
Get
Return mFirstName
End Get
Set(ByVal value As String)
If Not mFirstName = value Then
Dim args As New FirstNameChangedEventArgs(mFirstName, value, Me)
Me.OnFirstNameChanged(args)
mFirstName = value
End If
End Set
End Property
#End Region
#Region "Instance Public Methods"
Public Overrides Function ToString() As String
Return String.Concat(FirstName, " ", LastName)
End Function
Protected Overridable Sub OnLastNameChanged(ByVal e As LastNameChangedEventArgs)
RaiseEvent LastNameChanged(Me, e)
End Sub
Protected Overridable Sub OnFirstNameChanged(ByVal e As FirstNameChangedEventArgs)
RaiseEvent FirstNameChanged(Me, e)
End Sub
#End Region
#Region "Static Public Methods and Properties (NONE)"
'NONE
#End Region
#Region "Methods In Interfaces (NONE)"
'NONE
#End Region
#Region "Private Helper Methods (NONE)"
'NONE
#End Region
End Class
End Namespace
First Name Changed Event Args Class:
Code:
Namespace Utilities
<Serializable()> _
Public Class FirstNameChangedEventArgs
Inherits EventArgs
Sub New(ByVal oldFirstName As String, ByVal newFirstName As String, ByVal sendingPerson As Person)
mOldFirstName = oldFirstName
mNewFirstName = newFirstName
End Sub
Private mOldFirstName As String
Public ReadOnly Property OldFirstName() As String
Get
Return mOldFirstName
End Get
End Property
Private mNewFirstName As String
Public ReadOnly Property NewFirstName() As String
Get
Return mNewFirstName
End Get
End Property
Private mPerson As Person
Public ReadOnly Property Person() As Person
Get
Return mPerson
End Get
End Property
End Class
End Namespace
The last name changed event args class is similar to the above code
The Serialization Helpers Class is posted again in the next post because it was not accepted here due to it's size.
Re: [2005] Problem Serializing
The Serialization Helpers Class:
Code:
Imports System.Runtime.Serialization
Imports System.Reflection
Namespace Utilities
Public Class SerializationHelpers
''' <summary>
''' Helper routine for serializing objects.
''' (Place a call to this sub into the Serializable Classes Protected Overridable Sub GetObjectData)
''' i.e. place into: Protected Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext)
''' </summary>
''' <param name="info"></param>
''' <param name="context"></param>
''' <param name="obj"></param>
''' <remarks></remarks>
Public Shared Sub GetObjectDataHelper(ByVal info As SerializationInfo, ByVal context As StreamingContext, ByVal obj As Object)
'Get list of serializable members
Dim members() As MemberInfo = GetFilteredSerializableMembers(obj.GetType)
'Read the value of each member
Dim values() As Object = FormatterServices.GetObjectData(obj, members)
'Store in the SerializationInfo Object, using member name
For i As Integer = 0 To members.Length - 1
info.AddValue(members(i).Name, values(i))
Next
End Sub
''' <summary>
''' Helper routine for deserializing objects.
''' Place a call to this function into the Serializable Constructor;
''' i.e. place into: Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext))
''' </summary>
''' <param name="info"></param>
''' <param name="context"></param>
''' <param name="obj"></param>
''' <remarks></remarks>
Public Shared Sub ISerializableContructorHelper(ByVal info As SerializationInfo, ByVal context As StreamingContext, ByVal obj As Object)
'Get the list of serializable members for this object
'excluding members that are of type delegate (filtered)
'Note that members()(below) contains all types of members from the class passed into this sub via obj (byval obj as Object)
Dim members() As MemberInfo = GetFilteredSerializableMembers(obj.GetType)
Dim values(members.Length - 1) As Object
'Read the value for each member
For i As Integer = 0 To members.Length - 1
'retrieve the members from members() that are of type field
'and then get all field's values
Dim member As MemberInfo = members(i)
If member.MemberType = MemberTypes.Field Then
Dim membertype As Type = CType(member, FieldInfo).FieldType
values(i) = info.GetValue(member.Name, membertype)
End If
Next
'assign all serializable members in one operation
FormatterServices.PopulateObjectMembers(obj, members, values)
End Sub
''' <summary>
''' Return an array of serializable MemberInfo for a given type.
''' (Same as FormatterServices.GetSerializableMember, but FILTERS OUT delegate fields.)
''' </summary>
''' <param name="ty"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Shared Function GetFilteredSerializableMembers(ByVal ty As Type) As MemberInfo()
Dim al As New ArrayList
'Get the list of serializable members
For Each mi As MemberInfo In FormatterServices.GetSerializableMembers(ty)
'Add the mi object to the arraylist if it is NOT:
'(a FieldInfo object) [that IS also (AndAlso)] (found in the inheritance hierarchy of the Delegate class)
If Not (TypeOf mi Is FieldInfo AndAlso GetType([Delegate]).IsAssignableFrom(DirectCast(mi, FieldInfo).FieldType)) Then
al.Add(mi)
End If
Next
Return DirectCast(al.ToArray(GetType(MemberInfo)), MemberInfo())
End Function
End Class
End Namespace
Re: [2005] Problem Serializing
Due to the low number of replys to my original question in this thread, I assume that either the explanation was too lengthy or that not many people use custom serialization.
Therefore, I decided to post a complete example others may find useful in the future.
If anyone finds any errors in this code please let me know and I'll make corrections here for future viewers to see.
Thanks