Incubus
Incubus

Reputation: 46

vb.net serialization classes

I'm working on a project that will need an Save/load function. I'm looking at two possible ways:

In my current test project I implemented serialization, but when I update a class, the whole project is unreadable because it can't be processed by the deserialization... Making it very hard to use if I want files to be backwards compatible.

At this moment i use this approach:

<System.Serializable()> _
Public MustInherit Class clsCompo
    Implements ISerializable

    ...

    Public Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) Implements ISerializable.GetObjectData
        info.AddValue("properties", Me._properties, GetType(CustomPropertyCollection))
    End Sub

    Public Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        Me._properties = DirectCast(info.GetValue("properties", GetType(CustomPropertyCollection)), CustomPropertyCollection)
   End Sub
End Class

I'm quite lost in all this because I find it hard to handle, especially when using more than 30 different classes that all need to be serializable.

What I'm really after is a point in the "good" direction to implement a Save/Load feature that can be backwards compatible and can save different classes all in one file

EDIT: Eventually, I found a project similar to what I needed: http://www.codeproject.com/Articles/30270/XML-Serialization-of-Complex-NET-Objects

It's similar to what has been posted below.

Upvotes: 2

Views: 2688

Answers (2)

HeyHeyJC
HeyHeyJC

Reputation: 2867

The simplest method I've had reasonable long-term results with is to enforce (now and forever) your own contract that every class has at least a Version property, which when combined with the type gives you a unique identifier to later tell you how to deserialize to an object. You may also want to create objects from different classes depending on the version # of your stored data, so don't do the versioning within the class, do it with eg a Persister object which works on any object implementing your stateful interface:

First use an application-wide Enum for your application's fundamental object types (not each class):

Public Enum MyObjectTypesEnum
    Unspecified = 0
    Fish = 1
    Cow = 2
End Enum

Then your interface:

Public Interface IStateful
    ReadOnly Property Version As Integer
    ReadOnly Property ObjectType As MyObjectTypesEnum
    Sub SetState(Info As SerializationInfo)
    Sub GetState(Info As SerializationInfo)
End Interface

Step 1 in persisting your objects is to store the version and type:

Public Sub SaveTheBasics(Info As SerializationInfo, TheObject as IStateful)
    Info.SetInt64(TheObject.Version,"Version")
    Info.SetInt64(TheObject.ObjectType,"ObjectType")

This stores all the information you need to cope with different versions in the future:

Dim TheObject as IStateful

Select Case Info.GetInt64("ObjectType") 
Case MyObjectTypesEnum.Fish
    Select Case Info.GetInt64("Version")
    Case Is < 2
        TheObject = New OldestFishClass
    Case Is < 5
        TheObject = New OldFishClass
    Case Else
        TheObject = New NewFishClass
    End Select
Case MyObjectTypesEnum.Cow
    ...
End Select
TheObject.SetState(Info)

etc etc.

If your application's policy is to insist the data is stored in the latest version, then when loading and upgrading from a previous version's data, you can instantiate the old class, and use that in your newest class's overloaded loading method to upgrade:

    Select Case Info.GetInt64("Version")
    ...
    Case Is < 5
        Dim TempOldObject As New OldFishClass
        TempOldObject.SetState(Info)
        TheObject = New NewFishClass
        TheObject.SetState(TempOldObject)
    Case Else
        TheObject = New NewFishClass
        TheObject.SetState(Info)
    End Select
    ...        

...and make sure your new class knows how to load from its previous version as below.

You can use a base class which implements the Interface for all your stateful objects, and also has any properties all your objects may need:

Public MustInherit Class MyBaseClass
   Implements IStateful
   Public MustOverride Readonly Property VersionNumber As Integer Implements IStateful.Version
   Public MustOverride Readonly Property ObjectType As MyObjectTypesEnum Implements IStateful.ObjectType
   Public UniqueID As GUID

...

Public Class NewFishClass
    Inherits MyBaseClass
    Public Overrides ReadOnly Property VersionNumber As Integer
      Get
        Return 5
      End Get
    End Property
    Public Overrides ReadOnly Property ObjectType As MyObjectTypesEnum
      Get
        Return MyObjectTypesEnum.Fish
      End Get
    End Property
    'Overloaded state-setting methods (could be Constructors instead):
    Public Sub SetState(Info As SerializationInfo)
        Me.ScaleCount = Info.GetInt64("ScaleCount")
    End Sub
    Public Sub SetState(OldFish As OldFishClass)
        'Upgrade from V2:
        Me.ScaleCount = OldFish.LeftScales + OldFish.RightScales
    End Sub
    Public Sub SetState(OldestFish As OldestFishClass)
        'Upgrade from V1
        Me.ScaleCount = OldFish.Length * OldFish.ScalesPerUnitLength)
    End Sub
etc etc.

The overriding principle here is that the contract is contained in Interfaces, and as much as possible the base class does the necessary work of complying with that contract. Then to create your 30 classes you inherit each from the base object. If you've built it right you'll be forced to provide the information necessary for correct versioning and persistance (you're also building in abstraction layers, which of course at some point you'll be grateful for).

In larger projects I extend this sort of thinking so that each object's persistable properties are held in a "State" object which derives from a base "StateBase" class which itself implements an IState interface. Persistance then acts on any StateBase object. I also use a CollectionBase class (which also inherits from my base class) which handles instantiating and filling contained objects. Often your objects need to store references to each other across sessions, so everything needs a unique ID (GUID) to recreate those references - which goes in your base object.

Upvotes: 2

wolfeh
wolfeh

Reputation: 686

You need to develop your Data Access you receive xml from your stored procedures and stream into your objects through your DAL. Then take your stored procedure xml data and serializes it into the object.

Dim obj as New YourObjectType

obj = ConvertFromXml(xml.ToString(), GetType(YourObjectType) ,System.Text.Encoding.UTF8)

Public Shared Function ConvertFromXml(ByVal xml As String, ByVal objType As System.Type, ByVal encoding As System.Text.Encoding) As Object

    Dim o As Object = Nothing


    Dim serializer As XmlSerializer = New XmlSerializer(objType)
    Using ms As MemoryStream = New MemoryStream(encoding.GetBytes(xml))
        Using xr As XmlTextReader = New XmlTextReader(ms)
            o = serializer.Deserialize(xr)
        End Using
    End Using



    Return o
End Function

Upvotes: 0

Related Questions