Reputation: 2804
Why is not possible to Deserialize without providing output type?
Typically if two endpoints are communicating, both ends are sharing the same types. In my case i'm using exactly the same library on the client and on the server application. On startup i register all my types both on client and server like this:
var metaType = RuntimeTypeModel.Default.Add( type, false );
foreach( var prop in type.GetProperties() )
metaType.Add( i++, prop.Name );
and so i expect protobuf to try to understand what it should deserialize into without providing a type. At the moment i'm using this to deserialize:
RuntimeTypeModel.Default.DeserializeWithLengthPrefix(
stream, null, type, ProtoBuf.PrefixStyle.Fixed32, 0 );
This kind of behaviour (found in other serializer too) forces me to have a supertype that wraps every object i want to serialize and within which i have actual payload and some other info that help me understand how to deserialize the payload. So i can write something like this:
public class MySuperType
{
public byte[] Payload {get; set;}
public Type PayloadType {get; set;}
}
public object Deserialize( byte[] bytes )
{
using( var stream = new MemoryStream( bytes ) )
{
var wrapperObject = Serializer.DeserializeWithLengthPrefix<MySupertype>
( stream, ProtoBuf.PrefixStyle.Fixed32 );
using( var stream = new MemoryStream( wrapperObject.Payload ) )
{
var actualPayloadObject = RuntimeTypeModel.Default.DeserializeWithLengthPrefix(
stream, null, wrapperObject.PayloadType, ProtoBuf.PrefixStyle.Fixed32, 0 );
return actualPayloadObject;
}
}
}
So I basically know every object i receive is of type MySupertype. Am i complicating things out?
Upvotes: 2
Views: 390
Reputation: 1064324
It is not normal for a protobuf stream to indicate the root data type. It is expected that the calling code knows what the data looks like. However, there are other ways to approach the topic; for example, a common approach is to designate a tag number per type and use a slightly more complicated serialize / deserialize overload that performs this lookup - however, this still requires the code to configure the mapping between numbers / types.
A sometimes-more-convenient approach is often to use inheritance:
[ProtoContract]
[ProtoInclude(1, typeof(FooRequest))]
[ProtoInclude(2, typeof(BarRequest))]
public class RequestBase {}
public class FooRequest : RequestBase {...}
public class BarRequest : RequestBase {...}
This also avoids the need for Type
to be serialized, which is IMO not a good idea and can cause problems when versioning. With the above, you can use RequestBase
throughout - perhaps using polymorphism (virtual
or abstract
methods) for the interesting code.
Upvotes: 1