Protobuf-net与官方谷歌Protobuf for C ++(消息编码)不兼容

时间:2012-12-24 10:46:27

标签: c# c++ .net protocol-buffers protobuf-net

我们在.NET中有一些(很多)类。我们使用protobuf-net来标记它们,并通过google original library为C ++代码端生成.proto包装。

所以我有一条消息(C ++ DebugString()在某个EventBase类上(在.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 fileC++ file的大小相同)但内容不同:

enter image description here

你可以看到它的相似但不一样。 由于这种差异, CPP代码可以读取.NET C#消息而.NET无法读取CPP消息

在反序列化的代码中,我们得到:

  

发生了类型为“System.InvalidCastException”的未处理异常   在TestProto.exe中

     

附加信息:无法投射类型的对象   'TestProto.EventBase'键入'TestProto.EventCharacterMoved'。

代码如下:

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

让我们看看(jpa在评论中提到的)protoc --decode_raw选项:

enter image description here

这可能与我的CPP包装使用最新的google protobuf版本有关,而protobuf-net可能使用一些较旧的编码格式或类似的东西......

所以我想知道如何让.NET protobuf读取C ++消息(使得它能够解码相同的东西)?

或者至少如何使原始谷歌protobuf编码与.NET protobuf相同?

对于那些真正感兴趣并希望进入它的人zipped bundle with simplified example (VS 2010 solutions for C++ and C# code included)

2 个答案:

答案 0 :(得分:2)

编辑;这应该在r616及以上版本中修复。


我终于有机会看到这一点(对延迟道歉,但社会季节性假期要求介入)。我明白现在发生了什么。

基本上,数据在理论上是相同的;实际上归结为字段排序。从技术上讲,字段通常按升序编写,但可以按任何顺序预期。关于protobuf-net;对于不涉及继承的类型,无论顺序如何,它都可以正常工作。 protobuf规范没有定义继承,因此protobuf-net增加了对规范的支持(由于需求不断增加)另外。作为实现特征,它首先写入子类信息 (即,字段15,子类型,在字段10之前写入)。目前,在反序列化期间,它还首先期望子类型信息。这很少影响任何人,因为protobuf-net是唯一使用这种继承的实现,所以继承功能的使用大多只能在protobuf-net到protobuf-net的使用中看到。

在您的情况下,您正在使用.proto与CPP互操作;这意味着CPP代码将能够使用protobuf-net数据,但它可能有另一种方式的类型转换异常(基本上,它在获取第一个数据字段时开始构造具体类型)。 / p>

尽管很少出现问题,但这是需要修复的问题。我可以尝试在今天或明天晚些时候看一下。

选项:

  • 确保子类型字段始终低于任何数据字段
  • 如果您知道它需要子类型,请使用Merge API并传入所需类型的现有新对象 - 这将正确填充现有对象
  • 等一两天(希望!)使用build r616或更高版本进行正确修复
  • 使用互操作时避免继承(以及其他特定于实现的功能)
    • 请注意,您可以通过封装对没有继承的相同数据建模 - 并且它可以很愉快地工作;它具体是创建具体类型,这是问题
  • 在从CPP网站构建数据时,通过将其分为两部分来达到不合理的长度(意思是:我不认为这是一个实际的解决方案):
    • 首先使用 EventBase数据编写EventCharacterMoved并序列化;现在在一个单独的模型中用{strong>只 EventBase数据写一个TreeFloat,并序列化;这将模拟以所需顺序编写它们(protobuf流是可附加的) - 不是很漂亮

答案 1 :(得分:1)

这看起来非常类似于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确定

.NET protobuf的版本是否足够新用于合并该修复程序?