I seem to have a habit of changing classnames and/or namespaces as a project develops to make them more meaningful and to help make space for classes which would have benefited from another's name/namespace.
When using the binary formatter to serialize an instance of an object, it is important to note that the type information (assembly, namespace, classname) is stored with the data; as opposed to how the XML formatter works, where it is up to the developer to pass in the type, as a parameter, to deserialize.
This presents a problem when you rename a class/namespace, as the deserialization will fail.
One such occurance was when I was developing my bespoke CMS system, I had a namespace of:
This namespace contained the data types that each of my CMS controls would expose (to give a brief background, I have developed templateable CMS controls which deal with the persistence and editing of data, of a given custom type, by serializing the data using the binary formatter and then exposing the typed data though 'container.dataitem' to the developer when implementing the control on a site.)
Anyway, I changed this namespace to 'CwCms.ControlDataTypes' to make it more descriptive, forgetting that I already had some content stored under the old namespace in the database. This of course broke all the content that I had so far and not being one to redo content work, I decided to hack together a snippet of code that would loop through the raw binary data stored in the CMS database and fix the namespace.
Looking at the data in a hex editor, I can't say I had time to fully reverse engineer the format, but I got enough understanding to do the job. In summary here is what I noted:
There is a 0x16byte header, followed by the size of the assembly name, followed by the assembly name. In my case there were then 5bytes to skip, followed by the size of the fully qualified class name, followed by the fully qualified class name (being the namespace and classname).
Using this information, the following code replaces the target namespace with the new namespace, moves the data to create the new space for the longer namespace name and fixes the 'size of fully qualified classname':
Dim SIZEOF_HEADER As Integer = &H16
Dim SKIP_BYTES_AFTER_ASMNAME As Integer = 5
Dim oldNamespace As String = "CwCms.DataTypes"
Dim newNamespace As String = "CwCms.ControlDataTypes"
Dim sizeof_ASMname As Integer = rawCmsRecord.Value(SIZEOF_HEADER)
Dim start_of_classname_struct As Integer = SIZEOF_HEADER + 1 + sizeof_ASMname + SKIP_BYTES_AFTER_ASMNAME
Dim newRecord((rawCmsRecord.Value.Length - 1) + newNamespace.Length - oldNamespace.Length) As Byte
Dim offset As Integer = 0
For i As Integer = 0 To rawCmsRecord.Value.Length - 1
'copy the bytes to the new record
If i <> start_of_classname_struct Then
newRecord(i + offset) = rawCmsRecord.Value(i)
'weve reached the start of the classname struct. inject the custom stuff
Dim sizeof_classname As Integer = rawCmsRecord.Value(start_of_classname_struct)
Dim oldclassname As String = ""
For i2 = i + 1 To i + sizeof_classname
oldclassname &= ChrW(rawCmsRecord.Value(i2))
oldclassname = Right(oldclassname, oldclassname.Length - oldNamespace.Length)
Dim newFQCN As String = newNamespace & oldclassname
offset = newFQCN.Length - sizeof_classname
'overwrite the new length
newRecord(i) = CByte(newFQCN.Length)
'put the new classname on the end
Dim ctr As Integer = 0
For ctr = 1 To newFQCN.Length
newRecord(i + ctr) = CByte(AscW(newFQCN(ctr - 1)))
i += sizeof_classname
rawCmsRecord.Value = newRecord
As the format is not documented by Microsoft the format may change without notice and as I have mentioned this is only a quick interpretation of what I saw, to get the job done. Before implementing this code for yourself, check it will not break your data, I am not responsible!