Serialization idea / rant

  • Thread starter Thread starter John Wood
  • Start date Start date
J

John Wood

I'm just considering embarking on writing a class that does a better job
than the Serialize attribute. The serialize attribute is no good because it
sucks at even the most basic of versioning. So the idea is to write
something that uses reflection and a simple persistable named/value pairs
object (using BinaryWriter) to store the state of the object graph.

I'd rather not write it though... other than writing custom serialization,
what other options do people choose?
 
Oh well it was pretty simple to get something that works for me right now at
least.

Here's the code, very far from perfect. Any suggestions or enhancements are
welcome. For example it doesn't deal with many value types right now, but
does cope with deleting or adding fields to classes between sessions and
renaming classes.

---

public static byte[] Serialize(object graph)
{
MemoryStream ms = new MemoryStream();
BinaryWriter wr = new BinaryWriter(ms, System.Text.Encoding.Unicode);
foreach (FieldInfo fi in graph.GetType().GetFields())
{
if (!fi.IsNotSerialized)
{
// write out the name
wr.Write(fi.Name);

wr.Flush();
long lengthPosition = ms.Position;
// write out a dummy length
wr.Write((Int64)0);
long bookmark = ms.Position;

object val = fi.GetValue(graph);

// for primitive types, call the correctly overloaded Write operation
if (fi.FieldType.IsPrimitive || fi.FieldType==typeof(string))
wr.GetType().InvokeMember("Write", BindingFlags.InvokeMethod, null,
wr, new object[] { val });
else
// for more complex types, write out the serialized version of the
object
wr.Write(Serialize(val));

wr.Flush();
long length = ms.Position - (long)bookmark;
wr.Seek((int)lengthPosition, SeekOrigin.Begin); // jump back to the
bookmark to write out the length
// write out the length
wr.Write(length);
wr.Flush();
wr.Seek(0, SeekOrigin.End); // jump to the end
}
}
ms.Capacity = (int)ms.Position;
return ms.GetBuffer(); // ToArray could be used, but copies the buffer
}

public static void DeSerialize(object instance, byte[] buffer, bool
throwErrors)
{
MemoryStream ms = new MemoryStream(buffer);
BinaryReader reader = new BinaryReader(ms, System.Text.Encoding.Unicode);

while (ms.Position<ms.Length)
{
// read the name
string name = reader.ReadString();
// read the length
long len = reader.ReadInt64();
long bookmark = ms.Position;

try
{
// find the type...
FieldInfo fi = instance.GetType().GetField(name);
if (fi!=null && !fi.IsNotSerialized)
{
// get the type...
string typeName = fi.FieldType.FullName;
if (typeName.StartsWith("System."))
{
// primitive type, we can use the ReadXXX functions.
// first, get the primitive type name...
typeName = typeName.Substring(typeName.IndexOf(".")+1);
// then try to find a matching method in the reader
MethodInfo mi = reader.GetType().GetMethod("Read" + typeName);
if (mi==null) throw new ApplicationException("Unsupported type: " +
fi.FieldType.FullName);
object val = mi.Invoke(reader, new object[0]);

// set the value
fi.SetValue(instance, val);
}
else
{
// locate the object in question...
Type objectType = Type.GetType(typeName);
if (objectType!=null)
{
ConstructorInfo constructor =
objectType.GetConstructor(System.Type.EmptyTypes);
if (constructor==null) throw new ApplicationException("Class '" +
typeName + "' must support parameterless constructor");
// create a new instance
object obj = constructor.Invoke(new Object[0]);
byte[] subObjectBuffer = new byte[len];
// get the serialized data from the stream
reader.Read(subObjectBuffer, 0, (int)len);
// deserialize into the object
DeSerialize(obj, subObjectBuffer, throwErrors);
fi.SetValue(instance, obj);
}
else
{
throw new ApplicationException("The class '" + typeName + "' cannot
be resolved");
}
}
}
}
catch (Exception e)
{
// if there's an exception, throw error otherwise just skip it
if (throwErrors) throw (e);
}
// skip that data according to the length stored, in case the type has
changed
ms.Seek(len + bookmark, SeekOrigin.Begin);
}
}
}
 
You should always overwrite the method GetObjectData ob ISerializable to
serialize only the methods you want.
 
Back
Top