简体   繁体   English

Protobuf-net与C ++的官方Google Protobuf不兼容(消息编码)

[英]Protobuf-net is incompatible with official google Protobuf for C++ (message encoding)

We had some (lots of) classes in .NET. .NET中有一些(很多)类。 We used protobuf-net to mark them up, and generate .proto wrappers for C++ code side via google original library . 我们使用protobuf-net对其进行了标记,并通过google原始库为C ++代码端生成了.proto包装器。

So I have a message (C++ DebugString() on some EventBase class (in .NET EventCharacterMoved inherits EventBase while in C++ I just write to optional property)): 因此,我收到一条消息(某些EventBase类上的C ++ DebugString()(在.NET中, EventCharacterMoved继承了EventBase而在C ++中,我只是写入可选属性)):

UserId: -2792
EventCharacterMoved {
  Coordinates {
    Position {
      X: 196.41913
      Y: 130
      Z: 213
    }
    Rotation {
      X: 207
      Y: 130
      Z: 213
    }
  }
  OldCoordinates {
    Position {
      X: 196.41913
      Y: 130
      Z: 213
    }
    Rotation {
      X: 207
      Y: 130
      Z: 213
    }
  }
}

(From such .proto file) (来自.proto文件)

message Coordinates {
   optional TreeFloat Position = 1;
   optional TreeFloat Rotation = 2;
}
message EventBase {
   optional int32 UserId = 10 [default = 0];
   // the following represent sub-types; at most 1 should have a value
   optional EventCharacterMoved EventCharacterMoved = 15;
}
message EventCharacterMoved {
   optional Coordinates Coordinates = 100;
   optional Coordinates OldCoordinates = 101;
}
message TreeFloat {
   optional float X = 1 [default = 0];
   optional float Y = 2 [default = 0];
   optional float Z = 3 [default = 0];
}

In C++ I send this and we send the same message contents from .NET. 在C ++中,我发送此消息,然后从.NET发送相同的消息内容。

The C++ code can parse C++ encoded message as well as the .NET encoded one. C ++代码可以解析C ++编码的消息以及.NET编码的消息。 The .NET code can only parse the .NET message. .NET代码只能解析.NET消息。

Over the wire we get 87 bytes flying (same size from .Net file and C++ file ) yet contents are different: 通过电线,我们获得了87个字节的飞行空间( .Net文件C ++文件的大小相同),但内容有所不同:

在此处输入图片说明

As you can see its similar yet not same. 如您所见,其相似之处并不相同。 As a result of such difference CPP code can read .NET C# messages while .NET can not read CPP messages . 由于这种差异, CPP代码可以读取.NET C#消息,而.NET无法读取CPP消息

In code on deserialization we get: 在反序列化代码中,我们得到:

An unhandled exception of type 'System.InvalidCastException' occurred in TestProto.exe TestProto.exe中发生了'System.InvalidCastException'类型的未处理异常

Additional information: Unable to cast object of type 'TestProto.EventBase' to type 'TestProto.EventCharacterMoved'. 附加信息:无法将类型为“ TestProto.EventBase”的对象转换为类型为“ TestProto.EventCharacterMoved”。

in code like: 在像这样的代码中:

using (var inputStream = File.Open(@"./cpp_in.bin", FileMode.Open, FileAccess.Read)) {
    var ecm = Serializer.Deserialize<EventCharacterMoved>(inputStream);
}

Let's look at (as mentioned by jpa in his comment) protoc --decode_raw option: 让我们看一下(如jpa在他的评论中提到的) protoc --decode_raw选项:

在此处输入图片说明

This can be related to the fact that my CPP wrapper uses latest google protobuf version while protobuf-net probably uses some older encoding format or something like this... 这可能与以下事实有关:我的CPP包装器使用了最新的Google protobuf版本,而protobuf-net可能使用了一些较旧的编码格式或类似的格式...

So I wonder how to make .NET protobuf read C++ messages (make tham capable of decoding same stuff)? 因此,我想知道如何使.NET protobuf读取C ++消息(使tham能够解码相同内容)?

Or at least how to make original google protobuf encode same way .NET protobuf does? 或者至少如何使原始的Google protobuf编码与.NET protobuf相同?

And for those who are really interested and would like to get into it zipped bundle with simplified example (VS 2010 solutions for C++ and C# code included) 对于那些真正感兴趣并希望通过简化示例进入压缩包的用户(包括适用于C ++和C#代码的VS 2010解决方案)

Edit; 编辑; this should be fixed in r616 and above. 该问题应在r616及更高版本中修复。


I've finally had chance to look at this (apologies for delay, but social seasonal holiday demands intervened). 我终于有机会研究一下(为延迟而道歉,但社交季节性假期需求有所干预)。 I understand what is happening now. 我了解现在发生了什么。

Basically, the data is theoretically identical; 基本上,数据在理论上是相同的; what this actually comes down to is field-ordering. 这实际上归结为现场排序。 Technically, fields are usually written in ascending order, but can be expected in any order. 从技术上讲,字段通常按升序编写,但可以按任何顺序期望。 With regards to protobuf-net; 关于protobuf网; for types that don't involve inheritance it will work fine regardless of order. 对于不涉及继承的类型,无论顺序如何,它都可以正常工作。 The protobuf specification does not define inheritance, so protobuf-net adds support for that (due to constant demand) additionally to the specification. protobuf规范没有定义继承,因此protobuf-net在规范的基础上(由于不断的需求) 增加了对该继承的支持。 As an implementation feature, it writes the sub-class information first (ie field 15, the sub-type, is written ahead of field 10). 作为一种实施的功能时,它首先写入(即字段15,子类型,则提前写入字段10)的子类的信息。 At the current time, during deserialization it also expects the sub-type information first. 当前,在反序列化期间,它也首先需要子类型信息。 This has rarely impacted anyone, because since protobuf-net is the only implementation that uses inheritance like this, use of the inheritance feature is mostly only seen with protobuf-net to protobuf-net usage. 这几乎对任何人都没有影响,因为既然protobuf-net是唯一这样使用继承的实现,那么继承功能的使用通常仅在protobuf-net到protobuf-net的使用中才能看到。

In your case, you're using .proto to interop with CPP; 就您而言,您正在使用.proto与CPP互操作; which means the CPP code will be able to consume to protobuf-net data, but it may have a type-cast exception going the other way (basically, it starts constructing the concrete type at the time it gets the first data field). 这意味着CPP代码将能够使用protobuf-net数据,但是它可能会有类型转换异常(反之亦然)(基本上,它在获取第一个数据字段时便开始构造具体类型)。

Despite rarely being an issue, this is something that needs fixing. 尽管很少有问题,但这是需要解决的问题。 I can try to look at this later today or tomorrow. 我可以尝试今天晚些时候或明天看看。

Options: 选项:

  • make sure the sub-type fields are always lower than any data fields 确保子类型字段始终低于任何数据字段
  • if you know it is expecting the sub-type, use the Merge API and pass in an existing new object of the desired type - this will then populate the existing object correctly 如果您知道期望该子类型,请使用Merge API并传入所需类型的现有新对象-然后,它将正确填充现有对象
  • wait a day or two (hopefully!) use build r616 or above for a proper fix 等待一两天(希望如此!)使用内部版本r616或更高版本进行正确修复
  • avoid inheritance (and other implementation-specific features) when using interop 使用互操作时避免继承(以及其他实现特定的功能)
    • note you can model the same data without inheritance, via encapsulation - and it will work happily; 请注意,您可以通过封装对相同的数据进行建模而无需继承-它会很愉快地工作; it is specifically the creation of the concrete type that is the issue here 特别是具体类型的创建是这里的问题
  • go to unreasonable lengths (meaning: I don't consider this an actual solution) when constructing the data from the CPP site, by writing it in two pieces: 在CPP网站上构造数据时,请分两段来写一些不合理的长度(意思是:我认为这不是实际的解决方案):
    • write an EventBase with just the EventCharacterMoved data first, and serialize; EventBase 编写带有EventCharacterMoved数据的EventBase ,然后进行序列化; now in a separate model write an EventBase with just the TreeFloat data, and serialize; 现在在一个单独的模型中, 使用TreeFloat数据编写一个EventBase并进行序列化; this will simulate writing them in the required order (protobuf streams are appendable) - not pretty 这将模拟按要求的顺序编写它们(protobuf流是可附加的)-不太漂亮

This looks pretty similar to the problems noted in http://code.google.com/p/protobuf-net/issues/detail?id=299 and http://code.google.com/p/protobuf-net/issues/detail?id=331 which were allegedly fixed by http://code.google.com/p/protobuf-net/source/detail?r=595 这看起来与http://code.google.com/p/protobuf-net/issues/detail?id=299http://code.google.com/p/protobuf-net/issues中指出的问题非常相似/ detail?id = 331 ,据称由http://code.google.com/p/protobuf-net/source/detail?r=595修复。

Is the version of .NET protobuf you're using new enough to have incorporated that fix? 您使用的.NET protobuf版本是否足够新,可以合并该修复程序?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM