反序列化未知类型与protobuf网 [英] Deserialize unknown type with protobuf-net

查看:948
本文介绍了反序列化未知类型与protobuf网的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有2个应该发送系列化protobuf网消息发送到对方的网络应用程序。我可以序列化对象,并给他们,但是,我无法弄清楚如何反序列化接收的字节

我想这个反序列化,并将其与一个NullReferenceException失败。

  //其中MS是包含序列化的一个MemoryStream
//从网络字节数组。
Messages.BaseMessage消息=
  ProtoBuf.Serializer.Deserialize&所述; Messages.BaseMessage>(毫秒);

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

  //其中MS是一个MemoryStream和为messageType是
//标记Uint16。
类型t = Messages.Helper.GetMessageType(为messageType);
System.Reflection.MethodInfo方法=
  typeof运算(ProtoBuf.Serializer).GetMethod(反序列化)MakeGenericMethod(T)。
消息= method.Invoke(空,新的对象[] {} MS)为Messages.BaseMessage;

下面的功能我用在网络上发送消息:

 内部空隙发送(Messages.BaseMessage消息){
  使用(System.IO.MemoryStream毫秒​​=新System.IO.MemoryStream()){
    ProtoBuf.Serializer.Serialize(MS,消息);
    字节[] = messageTypeAndLength新的字节[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());
  }
}

本类与基类,我序列化:

  [Serializable接口,
ProtoContract,
ProtoInclude(50,typeof运算(的BeginRequest))]
抽象内部类BaseMessage
{
  [ProtoMember(1)]
  抽象的公共UINT16为messageType {搞定; }
}[序列化,
ProtoContract]
内部类的BeginRequest:BaseMessage
{
    [ProtoMember(1)]
    公众覆盖UINT16为messageType
    {
        {返回1; }
    }
}



固定的使用马克·Gravell的建议。我删除从只读属性ProtoMember属性。也转为使用SerializeWithLength preFIX。这是我现在有:

  [Serializable接口,
ProtoContract,
ProtoInclude(50,typeof运算(的BeginRequest))]
抽象内部类BaseMessage
{
  抽象的公共UINT16为messageType {搞定; }
}[序列化,
ProtoContract]
内部类的BeginRequest:BaseMessage
{
    公众覆盖UINT16为messageType
    {
        {返回1; }
    }
}

要接收的对象:

  //其中this.Ssl是一个SslStream。
BaseMessage消息=
  ProtoBuf.Serializer.DeserializeWithLength preFIX< BaseMessage>(
    。this.Ssl,protobuf的prefixStyle.Base128);

要发送的对象:

  //其中this.Ssl是一个SslStream和信息可以是任何
//从BaseMessage继承。
ProtoBuf.Serializer.SerializeWithLength preFIX< BaseMessage>(
  。this.Ssl,消息,protobuf的prefixStyle.Base128);


解决方案

第一;对网络使用情况,有 SerializeWithLength preFIX DeserializeWithLength preFIX 其中柄长为你(可选与标记)。在 MakeGenericMethod 看起来不错乍一看;这实际上是在绑非常接近挂起犯我一直在做,以实现一个RPC堆栈的工作:待定code 已被替代DeserializeWithLength preFIX 这需要(主要是)一个 Func键< INT,类型> ,解决了标签类型,使其更容易反序列化动态数据意外

如果消息类型实际上之间涉及继承 BaseMessage 的BeginRequest ,那么你不需要这个;它总是转到最顶部的合同类型层次结构中的上下工作的方式(由于某些钢丝详情)。

此外 - 我还没有机会来测试它,但下面可能会打乱它:

  [ProtoMember(1)]
公众覆盖UINT16为messageType
{
    {返回1; }
}

据标记为序列,但没有机制设置的值。也许这就是问题?请尝试删除 [ProtoMember] 在这里,因为我不知道这是有用的 - 它(据序列而言),很大程度上是在<$ C $重复C> [ProtoInclude(...)] 标记。

I have 2 networked apps that should send serialized protobuf-net messages to each other. I can serialize the objects and send them, however, I cannot figure out how to deserialize the received bytes.

I tried to deserialize with this and it failed with a NullReferenceException.

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

I am passing a header before the serialized bytes that contains message type ID, which I can use in a giant switch statement to return the expected sublcass Type. With the block below, I receive the error: 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;

Here's the function I use to send a message over the network:

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());
  }
}

This the class, with base class, I'm serializing:

[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; }
    }
}


Fixed using Marc Gravell's suggestion. I removed the ProtoMember attribute from the readonly properties. Also switched to using SerializeWithLengthPrefix. Here's what I have now:

[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; }
    }
}

To receive an object:

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

To send an object:

//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);

解决方案

First; for network usage, there is SerializeWithLengthPrefix and DeserializeWithLengthPrefix which handle length for you (optionally with a tag). The MakeGenericMethod looks OK at first glance; and this actually ties in very closely to the pending commit of the work I've been doing to implement an RPC stack: the pending code has an override of DeserializeWithLengthPrefix that takes (essentially) a Func<int,Type>, to resolve a tag to a type to make it easier to deserialize unexpected data on the fly.

If the message type actually relates to the inheritance between BaseMessage and BeginRequest, then you don't need this; it always goes to the top-most contract type in the hierarchy and works its way down (due to some wire details).

Also - I haven't had chance to test it, but the following might be upsetting it:

[ProtoMember(1)]
public override UInt16 messageType
{
    get { return 1; }
}

It is marked for serialization, but has no mechanism for setting the value. Maybe this is the issue? Try removing the [ProtoMember] here, since I don't this is useful - it is (as far as serialization is concerned), largely a duplicate of the [ProtoInclude(...)] marker.

这篇关于反序列化未知类型与protobuf网的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆