Results 1 to 11 of 11

Thread: Binary Serialization and Custom Collections

  1. #1

    Thread Starter
    Member
    Join Date
    Sep 2010
    Location
    Huntsville, AL
    Posts
    62

    Binary Serialization and Custom Collections

    I used the ideas from this post to create a custom collection of my custom class, so that I would be able to easily sort my collection. My project uses binary serialization to save everything (well the important stuff anyway) to a file. The problem is that my custom collection now breaks the serialization.

    I can't seem to figure out how to get the custom collection to serialize!

    Here is my code for the custom collection class:

    VB.NET Code:
    1. Imports System.ComponentModel
    2. Imports System.Runtime.Serialization
    3. Imports System.Runtime.Serialization.Formatters.Binary
    4.  
    5. <Serializable()> _
    6. Public Class FGSDataCollection
    7.     Inherits System.ComponentModel.BindingList(Of FGSData)
    8.  
    9.     Implements ISerializable
    10.  
    11. #Region "Initializer"
    12.     Public Sub New()
    13.         ' need this for new empty list
    14.     End Sub
    15. #End Region 'Initializer
    16.  
    17. #Region "Types"
    18.     Private Class FGSDataComparer
    19.         Implements System.Collections.Generic.IComparer(Of FGSData)
    20.  
    21.         Private prop As PropertyDescriptor
    22.         Private direction As ListSortDirection
    23.  
    24.         Public Function Compare(ByVal x As FGSData, ByVal y As FGSData) As Integer Implements IComparer(Of FGSData).Compare
    25.             Dim result As Integer = DirectCast(Me.prop.GetValue(x), IComparable).CompareTo(Me.prop.GetValue(y))
    26.  
    27.             If Me.direction = ListSortDirection.Descending Then
    28.                 result = -result
    29.             End If
    30.  
    31.             Return result
    32.         End Function
    33.  
    34.         Public Sub New(ByVal prop As PropertyDescriptor, ByVal direction As ListSortDirection)
    35.             Me.prop = prop
    36.             Me.direction = direction
    37.         End Sub
    38.     End Class
    39. #End Region 'Types
    40.  
    41. #Region "Variables"
    42.     Private _sort As String
    43.     Private _sortProperty As PropertyDescriptor
    44.     Private _sortDirection As ListSortDirection
    45. #End Region 'Variables
    46.  
    47. #Region "Properties"
    48.     Public Property Sort() As String
    49.         Get
    50.             Return Me._sort
    51.         End Get
    52.         Set(ByVal value As String)
    53.             Dim prop As PropertyDescriptor = Nothing
    54.             Dim direction As ListSortDirection
    55.  
    56.             Me.ParseSortClause(value, prop, direction)
    57.             Me._sort = value
    58.  
    59.             If prop Is Nothing Then
    60.                 Me.RemoveSortCore()
    61.             Else
    62.                 Me.ApplySortCore(prop, direction)
    63.             End If
    64.         End Set
    65.     End Property
    66.  
    67.     Protected Overrides ReadOnly Property SortDirectionCore() As System.ComponentModel.ListSortDirection
    68.         Get
    69.             Return Me._sortDirection
    70.         End Get
    71.     End Property
    72.  
    73.     Protected Overrides ReadOnly Property SortPropertyCore() As System.ComponentModel.PropertyDescriptor
    74.         Get
    75.             Return Me._sortProperty
    76.         End Get
    77.     End Property
    78.  
    79.     Protected Overrides ReadOnly Property SupportsSortingCore() As Boolean
    80.         Get
    81.             Return True
    82.         End Get
    83.     End Property
    84. #End Region 'Properties
    85.  
    86. #Region "Methods"
    87.     Protected Overrides Sub ApplySortCore(ByVal prop As PropertyDescriptor, ByVal direction As ListSortDirection)
    88.         Me._sortProperty = prop
    89.         Me._sortDirection = direction
    90.  
    91.         Dim upperBound As Integer = Me.Items.Count - 1
    92.         Dim items(upperBound) As FGSData
    93.  
    94.         Me.Items.CopyTo(items, 0)
    95.  
    96.         Array.Sort(items, New FGSDataComparer(prop, direction))
    97.  
    98.         For index As Integer = 0 To upperBound
    99.             Me.Items(index) = items(index)
    100.         Next
    101.  
    102.         Me.OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, 0))
    103.     End Sub
    104.  
    105.     Protected Overrides Sub RemoveSortCore()
    106.         Me._sortProperty = Nothing
    107.         Me._sortDirection = Nothing
    108.     End Sub
    109.  
    110.     Private Sub ParseSortClause(ByVal clause As String, _
    111.                                 ByRef prop As PropertyDescriptor, _
    112.                                 ByRef direction As ListSortDirection)
    113.         If clause IsNot Nothing AndAlso clause.Trim() <> String.Empty Then
    114.             Dim parts As String() = clause.Split(" "c)
    115.  
    116.             If parts.Length > 2 Then
    117.                 Throw New ArgumentException("Invalid sort clause")
    118.             End If
    119.  
    120.             For index As Integer = 0 To parts.GetUpperBound(0)
    121.                 parts(index) = parts(index).Trim()
    122.             Next
    123.  
    124.             prop = TypeDescriptor.GetProperties(GetType(FGSData))(parts(0))
    125.  
    126.             If prop Is Nothing Then
    127.                 Throw New ArgumentException("Invalid property name")
    128.             End If
    129.  
    130.             If parts.Length = 1 OrElse _
    131.                parts(1) = String.Empty OrElse _
    132.                String.Compare(parts(1), "ASC", True) = 0 Then
    133.                 direction = ListSortDirection.Ascending
    134.             ElseIf String.Compare(parts(1), "DESC", True) = 0 Then
    135.                 direction = ListSortDirection.Descending
    136.             Else
    137.                 Throw New ArgumentException("Invalid sort direction")
    138.             End If
    139.         End If
    140.     End Sub
    141. #End Region 'Methods
    142.  
    143. #Region "Serialization"
    144.     Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
    145.         ' This gets the data without error.
    146.         ' The list contains the correct number of items,
    147.         ' but each item is Nothing.
    148.         Dim data As List(Of FGSData) = info.GetValue("FGSData", GetType(List(Of FGSData)))
    149.         For Each fgs As FGSData In data
    150.             Me.Add(fgs)
    151.         Next
    152.  
    153.         ' I think I want to do this, but Me.Items is read only
    154.         'Me.Items = info.GetValue("FGSData", GetType(List(Of FGSData)))
    155.  
    156.         ' Ultimately, this is the part that is giving me problems.
    157.     End Sub
    158.  
    159.     Public Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) Implements ISerializable.GetObjectData
    160.         info.AddValue("FGSData", Me.Items)
    161.     End Sub
    162. #End Region 'Serialization
    163. End Class

  2. #2
    PowerPoster
    Join Date
    Mar 2002
    Location
    UK
    Posts
    4,780

    Re: Binary Serialization and Custom Collections

    If FGSData itself marked as serializable?

  3. #3

    Thread Starter
    Member
    Join Date
    Sep 2010
    Location
    Huntsville, AL
    Posts
    62

    Re: Binary Serialization and Custom Collections

    If FGSData itself marked as serializable?
    Good question. Yes, it is serializable. Before I implemented the custom collection class, I was using a generic List(Of FGSData), and the serialization for FGSData was working fine.

    Also, to be more specific, saving to a file appears to be working fine. I don't know how to actually check the binary file to be sure, but stepping through with the debugger shows the GetObjectData method of the FGSDataCollection class firing once, and then the GetObjectData method of the FGSData class firing for each item in the FGSDataCollection. This behavior makes sense to me. However, when I try to Deserialize (open) the file, only the GetObjectData method of the FGSDataCollection class fires. Additionally, when I try to use the debugger to view the contents in the deserialized collection a System.TypeLoadException is shown.

    I'm confident that it has to do with this section of code:

    VB.NET Code:
    1. Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
    2.         ' This gets the data without error.
    3.         ' The list contains the correct number of items,
    4.         ' but each item is Nothing.
    5.         Dim data As List(Of FGSData) = info.GetValue("FGSData", GetType(List(Of FGSData)))
    6.         For Each fgs As FGSData In data
    7.             Me.Add(fgs)
    8.         Next
    9.  
    10.         ' I want to do this, but Me.Items is read only
    11.         'Me.Items = info.GetValue("FGSData", GetType(List(Of FGSData)))
    12.     End Sub

    Or possibly the deserialize method in the FGSData class, which is:

    VB.NET Code:
    1. Public Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
    2.         _UnitNumber = info.GetString("_UnitNumber")
    3.         _TagNumber = info.GetString("_TagNumber")
    4.         _ProjectNumber = info.GetString("_ProjectNumber")
    5.         _PIDNumber = info.GetString("_PIDNumber")
    6.         _Service = info.GetString("_Service")
    7.         _FilledBy = info.GetString("_FilledBy")
    8.         _FilledDate = info.GetDateTime("_FilledDate")
    9.         _UnitDescription = info.GetString("_UnitDescription")
    10.         _ProjectDescription = info.GetString("_ProjectDescription")
    11.         _EquipmentType = info.GetInt32("_EquipmentType")
    12.         _EquipmentOther = info.GetString("_EquipmentOther")
    13.         _MaterialType = info.GetUInt64("_MaterialType")
    14.         _MaterialOther = info.GetString("_MaterialOther")
    15.         _Temperature = info.GetUInt16("_Temperature")
    16.         _Pressure = info.GetUInt16("_Pressure")
    17.         _Phase = info.GetUInt16("_Phase")
    18.         _GasWeight = info.GetUInt16("_GasWeight")
    19.         _H2SConcentration = info.GetUInt16("_H2SConcentration")
    20.         _Occupancy = info.GetUInt16("_Occupancy")
    21.         _IgnitionSources = info.GetUInt16("_IgnitionSources")
    22.         _IgnitionOther = info.GetString("_IgnitionOther")
    23.         _ProcessEnvironment = info.GetUInt16("_ProcessEnvironment")
    24.         _ProcessOther = info.GetString("_ProcessOther")
    25.         _Congestion = info.GetUInt16("_Congestion")
    26.         _IPLCredit = info.GetUInt16("_IPLCredit")
    27.         _Notes = info.GetString("_Notes")
    28.     End Sub

    I'm wondering if I need to call New() in the above method?

  4. #4
    PowerPoster
    Join Date
    Mar 2002
    Location
    UK
    Posts
    4,780

    Re: Binary Serialization and Custom Collections

    That is the new method above. Unless you are changing the format of objects between versions, you should not have to do any custom serialization at all. You can remove items easy enough, and add new ones with an attribute to allow you not to fail (er ... OptionalField?), so to replace a collection you could just remove the old name, create a new one of the correct type and save the custom handlers.

  5. #5

    Thread Starter
    Member
    Join Date
    Sep 2010
    Location
    Huntsville, AL
    Posts
    62

    Re: Binary Serialization and Custom Collections

    Yes, forget I mentioned calling the New() method. However, I don't understand what you're trying to explain. The tutorials I've read about using binary serialization of custom classes required implementing ISerializable, which in turn requires:

    VB.NET Code:
    1. Public Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
    2. End  Sub
    3.  
    4. Public Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) Implements ISerializable.GetObjectData
    5. End Sub

    Assuming the following imports:

    VB.NET Code:
    1. Imports System.Runtime.Serialization
    2. Imports System.Runtime.Serialization.Formatters.Binary
    have been imported.

  6. #6
    PowerPoster
    Join Date
    Mar 2002
    Location
    UK
    Posts
    4,780

    Re: Binary Serialization and Custom Collections

    Not at all, your mark as class as serializable, and then you serialize it . .Net works out all the types. The only time to get involved in customization (that I have found) is when you change a type and need to convert it differently, or when you have deleted 2 variables to combine into 1, ie 2 booleans into a single enum.

    A quick search came up with this, I'll extract the bits that make sense:

    Code:
    <Serializable()> Public Class MyTestClass
        Public Num1 As Integer
        Public Num2 As Integer
    End Class
    
    
    Dim ser As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
    
    Dim myObject As New MyTestClass With {Num1 = 12, Num2 = 23}
    Using fs As New IO.StreamWriter("c:\temp\myfilename.xml")
        ser.Serialize(fs, myObject)
    End Using
    
    Using fs As New IO.StreamReader("c:\temp\myfilename.xml")
        Dim myObjectCopy As MyTestClass = DirectCast(ser.Deserialize(fs), MyTestClass)
    End Using
    *note, just copied and edited, should work but hopefully you get the picture.
    Last edited by Grimfort; Aug 26th, 2011 at 02:14 PM.

  7. #7
    Smooth Moperator techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,522

    Re: Binary Serialization and Custom Collections

    one thing I've had issue with in the past regarding serializatoin is read-only properties. I could get them to serialize, but not deserialize (because the class didn't have a set method for the property). Now this was using xml serialization, not binary, so I don't know for sure if that's an issue with binary serialization. I was able to work around it by using the XMLSerializeIgnore attribute markup (or something along those lines). I then just had to make sure to expose the data in an alternate format for (de)serialization.

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  8. #8

    Thread Starter
    Member
    Join Date
    Sep 2010
    Location
    Huntsville, AL
    Posts
    62

    Re: Binary Serialization and Custom Collections

    Not at all, your mark as class as serializable, and then you serialize it . .Net works out all the types.
    I have found that this only works for serializing the default types/classes. From my experience, if you want to serialize a custom type or class, you're forced to dive into implementing the ISerializable interface.

    Additionally, I tried removing ISerializable from my custom collection class, and it still did not work.

    Nobody else has had problems trying to serialize a custom collection?

  9. #9
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,943

    Re: Binary Serialization and Custom Collections

    No. I have used binary serialization in a few different scenarios with custom classes and lists of those classes (and for a class that had a list of custom classes that each had a list of custom classes). I've never had to do more than mark all the classes as Serializable(). I've done this serialization in a single project, as well as in dlls for passing serialized custom classes between programs using a UDP connection.

    Frankly, it has always been so dead simple to implement that I really can't say why you are having this issue. When something always works the first and easiest way you try it, you tend not to learn all that much about it. All I can say is that you must be missing something. Perhaps you can describe what led you to conclude that you had to implement the ISerializable interface.
    My usual boring signature: Nothing

  10. #10

    Thread Starter
    Member
    Join Date
    Sep 2010
    Location
    Huntsville, AL
    Posts
    62

    Re: Binary Serialization and Custom Collections

    Okay, I've finally figured out how to deserialize my custom collection. I don't know if this is how it's supposed to be done, but it works. Here is the code:

    Code:
        Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
            ' retrieve the count from the file
            Dim iCnt As Integer = info.GetInt32("FGSDataCount")
    
            ' get each of the items from the file based on the count
            For i As Integer = 0 To iCnt - 1
                Me.Add(info.GetValue("FGSData" & i.ToString, GetType(FGSData)))
            Next
        End Sub
    
        Public Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) Implements ISerializable.GetObjectData
            ' store the count to the file
            info.AddValue("FGSDataCount", Me.Count)
    
            ' iterate through each item and add it to the file
            For i As Integer = 0 To Me.Count - 1
                info.AddValue("FGSData" & i.ToString, Me.Item(i))
            Next
        End Sub
    So, first I save the collection count to the file. Then, I iterate through the list of items in the collection, and add each one to the file, giving each a unique name (based on position in list). Then when I want to deserialize the file, I first retrieve the count from the file. Then, I loop from 0 to count and using the unique name, I add each item back to my collection.

  11. #11

    Thread Starter
    Member
    Join Date
    Sep 2010
    Location
    Huntsville, AL
    Posts
    62

    Re: Binary Serialization and Custom Collections

    Perhaps you can describe what led you to conclude that you had to implement the ISerializable interface.
    I wish I could remember where I read that I needed to do that. Clearly, I don't need to, so I'll make a copy of my project and remove all of the ISerializable interfaces and see what happens...let you guys know.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width