使用protobuf-net反序列化未知类型

Nic*_*yle 21 c# serialization protocol-buffers protobuf-net

我有2个网络应用程序,应该相互发送序列化的protobuf-net消息.我可以序列化对象并发送它们,但是,我无法弄清楚如何反序列化接收的字节.

我尝试使用它反序列化,并且失败并出现NullReferenceException.

// Where "ms" is a memorystream containing the serialized
// byte array from the network.
Messages.BaseMessage message =
  ProtoBuf.Serializer.Deserialize<Messages.BaseMessage>(ms);
Run Code Online (Sandbox Code Playgroud)

我在包含消息类型ID的序列化字节之前传递一个标头,我可以在一个巨大的switch语句中使用它返回预期的sublcass Type.使用下面的块,我收到错误:System.Reflection.TargetInvocationException ---> System.NullReferenceException.

//Where "ms" is a memorystream and "messageType" is a
//Uint16.
Type t = Messages.Helper.GetMessageType(messageType);
System.Reflection.MethodInfo method =
  typeof(ProtoBuf.Serializer).GetMethod("Deserialize").MakeGenericMethod(t);
message = method.Invoke(null, new object[] { ms }) as Messages.BaseMessage;
Run Code Online (Sandbox Code Playgroud)

这是我用来通过网络发送消息的功能:

internal void Send(Messages.BaseMessage message){
  using (System.IO.MemoryStream ms = new System.IO.MemoryStream()){
    ProtoBuf.Serializer.Serialize(ms, message);
    byte[] messageTypeAndLength = new byte[4];
    Buffer.BlockCopy(BitConverter.GetBytes(message.messageType), 0, messageTypeAndLength, 0, 2);
    Buffer.BlockCopy(BitConverter.GetBytes((UInt16)ms.Length), 0, messageTypeAndLength, 2, 2);
    this.networkStream.Write(messageTypeAndLength);
    this.networkStream.Write(ms.ToArray());
  }
}
Run Code Online (Sandbox Code Playgroud)

这个类,有基类,我正在序列化:

[Serializable,
ProtoContract,
ProtoInclude(50, typeof(BeginRequest))]
abstract internal class BaseMessage
{
  [ProtoMember(1)]
  abstract public UInt16 messageType { get; }
}

[Serializable,
ProtoContract]
internal class BeginRequest : BaseMessage
{
    [ProtoMember(1)]
    public override UInt16 messageType
    {
        get { return 1; }
    }
}
Run Code Online (Sandbox Code Playgroud)


修正了使用Marc Gravell的建议.我从readonly属性中删除了ProtoMember属性.也切换到使用SerializeWithLengthPrefix.这就是我现在拥有的:

[Serializable,
ProtoContract,
ProtoInclude(50, typeof(BeginRequest))]
abstract internal class BaseMessage
{
  abstract public UInt16 messageType { get; }
}

[Serializable,
ProtoContract]
internal class BeginRequest : BaseMessage
{
    public override UInt16 messageType
    {
        get { return 1; }
    }
}
Run Code Online (Sandbox Code Playgroud)

要接收对象:

//where "this.Ssl" is an SslStream.
BaseMessage message =
  ProtoBuf.Serializer.DeserializeWithLengthPrefix<BaseMessage>(
    this.Ssl, ProtoBuf.PrefixStyle.Base128);
Run Code Online (Sandbox Code Playgroud)

发送对象:

//where "this.Ssl" is an SslStream and "message" can be anything that
// inherits from BaseMessage.
ProtoBuf.Serializer.SerializeWithLengthPrefix<BaseMessage>(
  this.Ssl, message, ProtoBuf.PrefixStyle.Base128);
Run Code Online (Sandbox Code Playgroud)

Ale*_*sev 9

Serializer.NonGeneric.Deserialize(Type, Stream); //Thanks,  Marc.
Run Code Online (Sandbox Code Playgroud)

要么

RuntimeTypeModel.Default.Deserialize(Stream, null, Type); 
Run Code Online (Sandbox Code Playgroud)


Mar*_*ell 7

第一; 对于网络使用,有SerializeWithLengthPrefixDeserializeWithLengthPrefix哪个处理长度(可选择带标签).在MakeGenericMethod乍看上去OK; 这实际上与我为实现RPC堆栈所做的工作的挂起提交非常紧密地联系起来:挂起的代码has an override of DeserializeWithLengthPrefix(实质上)a Func<int,Type>,将标记解析为类型以便更容易反序列化意外数据在飞行中.

如果消息类型实际上与BaseMessage和之间的继承有关BeginRequest,那么你不需要这个; 它总是进入层次结构中最顶层的合同类型并且向下运行(由于一些电线细节).

另外 - 我没有机会测试它,但以下可能会让它感到不安:

[ProtoMember(1)]
public override UInt16 messageType
{
    get { return 1; }
}
Run Code Online (Sandbox Code Playgroud)

它标记为序列化,但没有设置值的机制.也许这就是问题?尝试删除[ProtoMember]这里,因为我不这很有用 - 它(就序列化而言),主要是[ProtoInclude(...)]标记的副本.