反序列化未知类型与protobuf网 [英] Deserialize unknown type with protobuf-net
问题描述
我有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,类型>
,解决了标签类型,使其更容易反序列化动态数据意外。 p>
如果消息类型实际上之间涉及继承 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屋!