定义了protobuf-net序列化所需的proto2消息类型?

Posted

技术标签:

【中文标题】定义了protobuf-net序列化所需的proto2消息类型?【英文标题】:defined proto2 message-type necessary for protobuf-net serialization? 【发布时间】:2016-09-27 03:44:59 【问题描述】:
protobuf-net v2.1.0 ( latest available as NuGet package ) 
Visual Studio 2015 v14 
C#

我没有使用 protogen.exe 从消息类型定义生成我的类

因为我有额外的要求,所以我编写了自己的生成器,它编写了以下类:

[Serializable]  
public partial class MyType: ProtoBuf.IExtensible                                   
                                                                                               
  [ProtoBuf.ProtoMember(1, IsRequired = false, Name = @"RegistrationDate", DataFormat = ProtoBuf.DataFormat.Default)]
  [System.ComponentModel.DefaultValue("1/1/0001 12:00:00 AM")]                                                          
  public DateTime RegistrationDate;                                                                         

  [ProtoBuf.ProtoMember(2, IsRequired = false, Name = @"RegistrationId", DataFormat = ProtoBuf.DataFormat.TwosComplement)]
  [System.ComponentModel.DefaultValue(null)]                                            
  public long? RegistrationId;

但是当我尝试序列化时:

var  myObject = new MyType(RegistrationDate="2016-01-01",RegistrationId=1);
var stream = new MemoryStream();
ProtoBuf.Serializer.Serialize(stream, myObject );

protobuf-net 抛出异常:

“System.InvalidOperationException”类型的异常发生在 protobuf-net.dll 但未在用户代码中处理

附加信息:类型不是预期的,没有合同可以 推断:MyNamespace.MyType

显然protobuf-net 期望类声明用原型合同属性装饰:

[Serializable]
[ProtoBuf.ProtoContract(Name=@"MyTypeProto")]   
public partial class MyType: ProtoBuf.IExtensible                                   

但是,由于我使用诸如 DateTimeint? 之类的类型,我不相信我可以创建兼容的 proto2 消息类型。

如何构造我的类以便protobuf-net 可以成功序列化?

更新:如果我添加 [Protobuf.ProtoContract] 属性,那么我可以序列化/反序列化而不会引发异常,但我的反序列化对象丢失了所有值(所有字段均为 null):

[Serializable]  
[Protobuf.ProtoContract]
public partial class MyType: ProtoBuf.IExtensible                                   
                                                                                               
  [ProtoBuf.ProtoMember(1, IsRequired = false, Name = @"RegistrationDate", DataFormat = ProtoBuf.DataFormat.Default)]
  [System.ComponentModel.DefaultValue("1/1/0001 12:00:00 AM")]                                                          
  public DateTime RegistrationDate;                                                                         

  [ProtoBuf.ProtoMember(2, IsRequired = false, Name = @"RegistrationId", DataFormat = ProtoBuf.DataFormat.TwosComplement)]
  [System.ComponentModel.DefaultValue(null)]                                            
  public long? RegistrationId;

  var  myObject = new MyType(RegistrationDate="2016-01-01",RegistrationId=1);
  var stream = new MemoryStream();
  ProtoBuf.Serializer.Serialize(stream, myObject );
  var deserialized = ProtoBuf.Serializer.Deserialize<MyType>(stream);
  // deserialized.RegistrationDate is null
  // deserialized.RegistrationId is null

【问题讨论】:

【参考方案1】:

它所要求的只是告诉它你的类型确实是用于 protobuf 序列化的;所以:

[ProtoBuf.ProtoContract]   
public partial class MyType ...

名称完全是可选的。事实上,protobuf-net 的大多数 使用是代码优先而不是模式优先(甚至是模式优先),所以我敢打赌,不寻常明确的名称。

不过,作为旁注; DateTimeint? 都可以用 .proto 表示(尽管 int?DateTime 干净得多 - 它只是变成了一个 optional 字段,如果你选择从类型模型生成模式,您不需要)。

它不承认[Serializable],因为这意味着完全不同的东西,甚至不是在所有平台上都可用。

【讨论】:

谢谢马克。但是 DateTime 和 int 怎么可能呢?允许在 proto2 中使用吗?我认为 proto3 Timestamp 和 IntValue 类型是专门为了弥补 proto2 的这些不足而引入的。 @BaltoStar 我没有说它使用了 proto 3 的 Timestamp ;) 而 optional int 在 proto2 中很好;问题是 proto3 从语法中删除了“可选”(但:不会改变线路格式) 好吧,这是我的最新理解:对于纯 .NET 实现(线路两侧的 .NET)protobuf-net 不需要消息类型的合同——至少不像 .proto 中明确表达的那样文件。实际上,此类合约可能会不必要地增加复杂性并引起混淆(例如,当需要原始版本不支持的类型时)。最好让语言对象模型隐式表达合同。正确吗? 如果为了更好地记录我的合同,我确实想创建包含 DateTime 类型的 proto2 消息类型——那么为了使 protogen.exe 正常工作,我需要包含 bcl.proto ,正确? @BaltoStar yar,DateTime 真的是为 .net-.net 设计的;如果你想要更好的外部支持,我会推荐一个ulong shim (例如)unix 时间

以上是关于定义了protobuf-net序列化所需的proto2消息类型?的主要内容,如果未能解决你的问题,请参考以下文章

序列化动态类型参数 Protobuf-net

名为“名称”的操作不符合所需的签名。参数类型必须是实体类型或预定义的可序列化类型之一

protobuf-net 中没有为 System.Version 定义序列化程序

使用自定义十进制原型合约(C#/C++ 互操作)时,Protobuf-net(反)序列化小数抛出

对象(通用)的 Protobuf-net 序列化抛出错误没有为类型定义序列化程序:System.Object

在 protobuf-net 中,有没有办法指定在序列化/反序列化给定类型时要使用的自定义方法?