我们有一些(很多).NET 类。我们使用了 protobuf-net标记它们,并通过 google original library 为 C++ 代码端生成 .proto 包装器.
所以我有一条消息(某些 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
}
}
}
(来自这样的 .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];
}
在 C++ 中,我发送这个消息,我们从 .NET 发送相同的消息内容。
C++ 代码可以解析 C++ 编码的消息以及 .NET 编码的消息。 .NET 代码只能解析 .NET 消息。
通过网络,我们得到 87 个字节(与 .Net file 和 C++ file 大小相同)但内容不同:
如您所见,它相似但不相同。 由于存在这种差异,CPP 代码可以读取 .NET C# 消息,而 .NET 无法读取 CPP 消息。
在反序列化代码中我们得到:
An unhandled exception of type 'System.InvalidCastException' occurred in TestProto.exe
Additional information: Unable to cast object of type 'TestProto.EventBase' to type 'TestProto.EventCharacterMoved'.
代码如下:
using (var inputStream = File.Open(@"./cpp_in.bin", FileMode.Open, FileAccess.Read)) {
var ecm = Serializer.Deserialize<EventCharacterMoved>(inputStream);
}
让我们看看(正如 jpa 在他的评论中提到的)protoc --decode_raw
选项:
这可能与我的 CPP 包装器使用最新的 google protobuf 版本而 protobuf-net 可能使用一些较旧的编码格式或类似的格式有关......
所以我想知道如何让 .NET protobuf 读取 C++ 消息(让它们能够解码相同的东西)?
或者至少如何使原始 google protobuf 以与 .NET protobuf 相同的方式编码?
对于那些真正感兴趣并想进入它的人zipped bundle with simplified example (VS 2010 solutions for C++ and C# code included)
最佳答案
编辑;这应该在 r616 及更高版本中修复。
我终于有机会看看这个(很抱歉延迟,但社会季节性假期需求干预)。我明白现在发生了什么。
基本上,数据理论上是一致的;这实际上归结为字段排序。从技术上讲,字段通常按升序排列,但也可以按任何顺序排列。关于 protobuf-net;对于不涉及继承的类型,无论顺序如何,它都可以正常工作。 protobuf 规范未定义继承,因此 protobuf-net 添加了对该规范的支持(由于不断的需求)另外到规范中。作为一个实现特征,它首先写入子类信息(即字段 15,子类型,在字段 10 之前写入)。目前,在反序列化过程中,它还期望首先得到子类型信息。这很少影响到任何人,因为由于 protobuf-net 是唯一使用这种继承的实现,继承功能的使用大多只在 protobuf-net 到 protobuf-net 的使用中看到。
在您的情况下,您正在使用 .proto 与 CPP 互操作;这意味着 CPP 代码将能够使用 protobuf-net 数据,但它可能有一个类型转换异常(基本上,它在获得第一个数据字段时开始构建具体类型)。
尽管这很少成为问题,但这是需要解决的问题。我可以在今天晚些时候或明天尝试查看此内容。
选项:
- 确保子类型字段始终低于任何数据字段
- 如果您知道它需要子类型,请使用 Merge API 并传入所需类型的现有新对象 - 然后这将正确填充现有对象
等待一两天(希望如此!)使用 build r616 或更高版本进行适当修复- 在使用互操作时避免继承(和其他特定于实现的特性)
- 请注意,您可以通过封装对相同的数据建模而无需继承 - 它会很愉快地工作;具体类型的创建才是这里的问题
- 在从 CPP 站点构建数据时,通过将其分为两部分来编写不合理的长度(意思是:我不认为这是一个实际的解决方案):
- 首先用仅
EventCharacterMoved
数据编写一个EventBase
,然后序列化;现在在一个单独的模型中编写一个EventBase
,只是TreeFloat
数据,并序列化;这将模拟按要求的顺序编写它们(protobuf 流是可附加的)——不漂亮
- 首先用仅
关于c# - Protobuf-net 与 C++ 的官方谷歌 Protobuf 不兼容(消息编码),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14020284/