Is there a possibility to skip the next 'entry' of a serialized stream when deserializing? Regarding a plugin-oriented architecture it can happen that distinct parts of a serialized object graphs might become unknown types in another environment (assume they could be safely ignored). Trying to deserialize these will of course fail.
abstract class Thing{}
class OneThing : Thing {} // <-- known in environment A & B
class SomeThing : Thing {} // <-- only known in environment A
...
var things = new List<Thing>();
...
things.Add( (OneThing)(formatter.Deserialize(stream)) );
things.Add( (SomeThing)(formatter.Deserialize(stream)) ); // <-- skip in B
things.Add( (OneThing)(formatter.Deserialize(stream)) );
How to get this working with the binary formatter? Do I have to calculate the length and retrieve an unambigous type-name (e.g. as string) of the serialized entry and store it right before the entry itself, so I can skip over it when deserializing (by incrementing the stream pointer)? Or is there a better alternative with less problem specific manipulation of the serialized representation?
I tried the version with simple skipping over the stream parts by incrementing the pointer, which did the trick. For the moment this works for me (allthough it might be not the nicest solution):
interface ISerializableObject { }
class PluginSerialization
{
private readonly IFormatter formatter;
public PluginSerialization(IFormatter f)
{
formatter = f;
}
public void SerializeToStream(IEnumerable<ISerializableObject> components, Stream s)
{
foreach (var component in components)
{
using (var cStream = new MemoryStream())
{
formatter.Serialize(cStream, component);
cStream.Flush();
// write to stream [length] [type as string] [object]
formatter.Serialize(s, cStream.Length);
formatter.Serialize(s, component.GetType().ToString());
cStream.WriteTo(s);
}
}
}
public List<ISerializableObject> DeserializeFromStream(Stream s, Func<string, bool> isKnownType )
{
var components = new List<ISerializableObject>();
while (s.Position < s.Length - 1)
{
var length = (long)(formatter.Deserialize(s));
var name = (string)(formatter.Deserialize(s));
// skip unknown types
if (!isKnownType(name))
{
s.Position += length;
continue;
}
components.Add((ISerializableObject) (formatter.Deserialize(s)));
}
return components;
}
}
This allows for partial deserialization of a list of different objects (List<ISerializableObject>()
). However, the way and order the data is stored (length, type name, object) is a specific implementation detail and therefore should be encapsulated as best as possible.