protobuf网将不序列化从Protobuf.js数据 [英] Protobuf-net won't deserialize data from Protobuf.js

查看:824
本文介绍了protobuf网将不序列化从Protobuf.js数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我用的protobuf我的Web客户端和服务器(C#)之间的通信,使用的WebSocket。在客户端,去/序列化是通过 Protobuf.js 并完成,在服务器上,使用protobuf网。

I'm using Protobuf for the communication between my web client and server (C#), using WebSocket. On the client, the de/serialization is done through Protobuf.js and, on the server, using protobuf-net.

问题在于,使用聚合与抽象类时,protobuf网不能反序列化由Protobuf.js发送的数据。

The problem is that, when using aggregation with abstract classes, protobuf-net can't deserialize the data sent by Protobuf.js.

这是堆栈跟踪:

ProtoException: No parameterless constructor found for Base.
at ProtoBuf.Meta.TypeModel.ThrowCannotCreateInstance(Type type) na c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 1397
at proto_6(Object , ProtoReader )
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read(Object value, ProtoReader source) na c:\Dev\protobuf-net\protobuf-net\Serializers\CompiledSerializer.cs:line 57
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source) na c:\Dev\protobuf-net\protobuf-net\Meta\RuntimeTypeModel.cs:line 775
at ProtoBuf.ProtoReader.ReadTypedObject(Object value, Int32 key, ProtoReader reader, Type type) na c:\Dev\protobuf-net\protobuf-net\ProtoReader.cs:line 579
at ProtoBuf.ProtoReader.ReadObject(Object value, Int32 key, ProtoReader reader) na c:\Dev\protobuf-net\protobuf-net\ProtoReader.cs:line 566
at proto_2(Object , ProtoReader )
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read(Object value, ProtoReader source) na c:\Dev\protobuf-net\protobuf-net\Serializers\CompiledSerializer.cs:line 57
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source) na c:\Dev\protobuf-net\protobuf-net\Meta\RuntimeTypeModel.cs:line 775
at ProtoBuf.Meta.TypeModel.DeserializeCore(ProtoReader reader, Type type, Object value, Boolean noAutoCreate) na c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 700
at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type, SerializationContext context) na c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 589
at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type) na c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 566
at ProtoBuf.Serializer.Deserialize[T](Stream source) na c:\Dev\protobuf-net\protobuf-net\Serializer.cs:line 77
at ProtobufPolymorphismTest.Program.Main(String[] args) na c:\Desenvolvimento\Testes\ProtobufPolymorphismTest\ProtobufPolymorphismTest\Program.cs:line 30
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

这是合同:

[ProtoContract]
[ProtoInclude(100, typeof(Child))]
abstract class Base
{
    [ProtoMember(1)]
    public int BaseProperty { get; set; }
}

[ProtoContract]
class Child : Base
{
    [ProtoMember(1)]
    public float ChildProperty { get; set; }
}

[ProtoContract]
class Request
{
    [ProtoMember(1)]
    public Base Aggregate { get; set; }
}

这是code重现错误。由于序列化的工作,我只是提供结果作为字节数组。如果有帮助,我可以提供我把步骤获得的序列化值。

And this is the code to reproduce the error. As the serialization is working, I'm only providing the result as a byte array. If it helps, I can provide the steps I took to get to the serialized values.

// This is the object serialized
Child child = new Child() { ChildProperty = 0.5f, BaseProperty = 10 };
Request request = new Request() { Aggregate = child };

// This is the byte representation generated by protobuf-net and Protobuf.js
byte[] protoNet = new byte[] { 10, 10, 162, 6, 5, 13, 0, 0, 0, 63, 8, 10 };
byte[] protoJS = new byte[] { 10, 10, 8, 10, 162, 6, 5, 13, 0, 0, 0, 63 };

// Try to deserialize the protobuf-net data
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(protoNet))
{
    request = Serializer.Deserialize<Request>(ms); // Success
}

// Try to deserialize the Protobuf.js data
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(protoJS))
{
    request = Serializer.Deserialize<Request>(ms); // ProtoException: No parameterless constructor found for Base.
}

如果我想补充SkipConstructor =真在基类的定义,该错误会更改为MemberAccessException:无法创建一个抽象类和以下堆栈跟踪。如果我删除了抽象基类的定义,它工作正常。

If I add SkipConstructor = true on the Base class definition, the error changes to "MemberAccessException: Cannot create an abstract class" with the following stack trace. If I remove the abstract from the Base class definition, it works as expected.

System.MemberAccessException: Cannot create an abstract class.
at System.Runtime.Serialization.FormatterServices.nativeGetUninitializedObject(RuntimeType type)
at ProtoBuf.BclHelpers.GetUninitializedObject(Type type) na c:\Dev\protobuf-net\protobuf-net\BclHelpers.cs:line 38
at proto_6(Object , ProtoReader )
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read(Object value, ProtoReader source) na c:\Dev\protobuf-net\protobuf-net\Serializers\CompiledSerializer.cs:line 57
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source) na c:\Dev\protobuf-net\protobuf-net\Meta\RuntimeTypeModel.cs:line 775
at ProtoBuf.ProtoReader.ReadTypedObject(Object value, Int32 key, ProtoReader reader, Type type) na c:\Dev\protobuf-net\protobuf-net\ProtoReader.cs:line 579
at ProtoBuf.ProtoReader.ReadObject(Object value, Int32 key, ProtoReader reader) na c:\Dev\protobuf-net\protobuf-net\ProtoReader.cs:line 566
at proto_2(Object , ProtoReader )
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read(Object value, ProtoReader source) na c:\Dev\protobuf-net\protobuf-net\Serializers\CompiledSerializer.cs:line 57
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source) na c:\Dev\protobuf-net\protobuf-net\Meta\RuntimeTypeModel.cs:line 775
at ProtoBuf.Meta.TypeModel.DeserializeCore(ProtoReader reader, Type type, Object value, Boolean noAutoCreate) na c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 700
at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type, SerializationContext context) na c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 589
at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type) na c:\Dev\protobuf-net\protobuf-net\Meta\TypeModel.cs:line 566
at ProtoBuf.Serializer.Deserialize[T](Stream source) na c:\Dev\protobuf-net\protobuf-net\Serializer.cs:line 77
at ProtobufPolymorphismTest.Program.Main(String[] args) na c:\Desenvolvimento\Testes\ProtobufPolymorphismTest\ProtobufPolymorphismTest\Program.cs:line 30
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

我不知道为什么二重presentation通过protobuf网和Protobuf.js不同产生的,但他们似乎都有效,因为它的工作原理如果基类不是抽象的。

I'm not sure why the binary representation generated through protobuf-net and Protobuf.js are different, but they both seem valid as it works if the Base class is not abstract.

为什么任何想法正在发生的事情或解决,而不从基类中删除抽象的方式?

Any ideas on why this is happening or a way to work around without removing the abstract from Base class?

在此先感谢!

更新

这是在code我用来产生通过Protobuf.js字节序列:

This is the code I used to generate the byte serialization through Protobuf.js:

<script src="//raw.githubusercontent.com/dcodeIO/ByteBuffer.js/master/dist/ByteBufferAB.min.js"></script>
<script src="//cdn.rawgit.com/dcodeIO/ProtoBuf.js/master/dist/ProtoBuf.js"></script>
<script type="text/javascript">
    // Proto file
    var proto = "";
    proto += "package ProtobufPolymorphismTest;\r\n\r\n";
    proto += "message Base {\r\n";
    proto += "    optional int32 BaseProperty = 1 [default = 0];\r\n";
    proto += "    // the following represent sub-types; at most 1 should have a value\r\n";
    proto += "    optional Child Child = 100;\r\n";
    proto += "}\r\n\r\n";
    proto += "message Child {\r\n";
    proto += "    optional float ChildProperty = 1 [default = 0];\r\n";
    proto += "}\r\n\r\n";
    proto += "message Request {\r\n";
    proto += "    optional Base Aggregate = 1;\r\n";
    proto += "}";

    // Build the entities
    var protoFile = dcodeIO.ProtoBuf.loadProto(proto);
    var requestClass = protoFile.build("ProtobufPolymorphismTest.Request");
    var baseClass = protoFile.build("ProtobufPolymorphismTest.Base");
    var childClass = protoFile.build("ProtobufPolymorphismTest.Child");

    // Build the request
    var base = new baseClass();
    base.BaseProperty = 10;
    base.Child = new childClass();
    base.Child.ChildProperty = 0.5;
    var request = new requestClass();
    request.Aggregate = base;

    // Serialize
    var bytes = new Uint8Array(request.toArrayBuffer());
    var str = "new byte[] { " + bytes.join(", ") + " };";
    console.log(str);
</script>

解决方法

正如马克解释说,protobuf网不支持多态时,场顺序相反。作为一种变通方法,具体到Protobuf.js,您可以更改.proto文件中的字段的顺序为它以正确的顺序序列。

As Marc explained, protobuf-net doesn't support polymorphism when the field order is inverted. As a workaround, specific to Protobuf.js, you can change the order of the fields in the .proto file for it to serialize in the correct order.

在我的情况下,改变.proto文件到以下解决了这个问题:

In my case, changing the .proto file to the following solved the problem:

package ProtobufPolymorphismTest;

message Base {
   // the following represent sub-types; at most 1 should have a value
   optional Child Child = 100;
   optional int32 BaseProperty = 1 [default = 0];
}
message Child {
   optional float ChildProperty = 1 [default = 0];
}
message Request {
   optional Base Aggregate = 1;
}

(注意选购儿童儿童= 100; 的的 BaseProperty

推荐答案

长期和短期的它是protobuf网的polymorpism支持,预计将第一消息中的分型(或更具体地说:任何对象,该类型将数据提供之前固定的)。在JS输出,现场数据 BaseProperty 来自第一 - 完全合理的,也许。但是,由于没有包罗万象的协议传承应该如何行为的定义,protobuf网的实现只是真的曾经打算与自己的工作。在字节而言,这实际上归结为其中场标记162,6(和相关联的长度/数据,5,13,0,0,0,63)的显示。

The long and short of it is that protobuf-net's polymorpism support expects the sub-type to be first in the message (or more specifically: for any object, the type will be fixed before data is provided). In the js output, the field data for BaseProperty comes first - perfectly reasonably, perhaps. But since there is no over-arching protocol definition of how inheritance should behave, protobuf-net's implementation was only really ever intended to work with itself. In terms of the bytes, this actually comes down to where the field marker "162, 6" (and the associated length/data, "5, 13, 0, 0, 0, 63") appears.

该库的可以的可能被修改,以允许任何字段顺序多态,但是:它会采取一些努力。我知道,这是的一般的预期来处理任何顺序领域,但由于这已经是规范之外,我并没有专注于这一点。所有其他数据字段都接受以任意顺序 - 只有多态性以这种方式工作

The library could potentially be reworked to allow any field order for polymorphism, but: it would take some effort. I am aware that it is usually expected to process fields in any order, but since this is already outside of the specification, I didn't focus on this. All other data fields are accepted in any order - only polymorphism works this way.

在一般情况下:,因为的多态性是不规范的一部分,我会强烈建议图书馆之间工作时,避免了多态。

In the general case: since polymorphism is not part of the specification, I would strongly recommend avoiding polymorphism when working between libraries.

请注意:你可能迫使这确保多态性领域的合作的(数字),比的数据字段

Note: you can probably force this to work by ensuring the polymorphism fields are lower (numerically) than the data fields.

这篇关于protobuf网将不序列化从Protobuf.js数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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