protobuf-net: 包含空元素的列表

4
public MyType {

  [ProtoMember(1)]
  public int Index;

  [ProtoMember(2)]
  public string Name;

  public MyType() {
  }

  public MyType(int index, string name) {
    Index = index;
    Name = name;
  }
}

var element0 = new MyType(0,"element index 0");
var element1 = null;
var element2 = new MyType(2,"element index 2");
var list = new List<MyType> {element0, element1, element2}

由于元素索引1为空,protobuf-net序列化程序将忽略序列化字节中的该元素。

在另一端,发生了非常有趣的事情:

protobuf-net反序列化程序读取字节,得知应该有3个元素,但只找到了索引0和2的元素字节。因此,它创建了一个空的MyType实例作为元素索引1。

因此,反序列化的列表等效于:

var element0 = new MyType(0,"element index 0");   
var element1 = new MyType(0,null); 
var element2 = new MyType(2,"element index 2");
var list = new List<MyType> {element0, element1, element2}

但这并不是预期的结果。Null并不等同于空对象,而具有重复Index=0和Name=null元素的存在可能会对处理代码产生严重的不良影响——这些代码理应不知道或关心序列化/反序列化的细节。
有解决方法吗?
1个回答

2
基本上,不,这是无法绕过的,并且在protobuf-net中这也不是一个支持的场景。底层的协议缓冲区格式(protobuf-net实现的)没有空的概念;我无法在常规格式内表示一个空实例。类列表只是一组重复的长度前缀节点,因此(在密集的二进制格式中,而不是文本格式):
[field 1, length-prefixed] [length prefix] [payload for element index 0]
[field 1, length-prefixed] [length prefix] [payload for element index 1]
[field 1, length-prefixed] [length prefix] [payload for element index 2]
... etc

我在列表中处理空值的唯一两种选项是:
  • 忽略它们
  • 将它们视为零长度
Protobuf-net 目前使用第二种方法,但是:零长度类本质上相当于 new MyType();在协议缓冲区中,零长度是完全有效和明确定义的。
与某些其他格式不同,我实际上没有任何地方可以添加额外的元数据来表示 “我是 null”(例如,在 xml 中的 @xsi:nil)。
也许我可以将此功能添加到“保留引用”代码中,但是我已经检查过,目前不支持空值-我承认这是一个小疏忽,但我还要强调,“保留引用”代码并不是标准的 protobuf,并且任何其他库(如 java、golang 等)可能难以使用该配置;我仅建议如果您知道只使用 protobuf-net 与 protobuf-net 进行通信。当然,空值支持目前不存在,需要进行修改 :)

谢谢Mark。在你的另一篇帖子中,我找到了这个解决方法:RuntimeTypeModel.Default[typeof(YourType)][1].SupportNull = true; 这个方法还被支持吗?如果是,您建议使用它吗?有什么优缺点? - BaltoStar
@BaltoStar 哎呀,我都忘了那件事了!随便试试吧 - 如果可以的话告诉我一声! - Marc Gravell
谢谢Mark。有计划将此功能作为代理类注释添加吗? - BaltoStar
在测试设置上下文中,当我为多个类型配置空支持时 RuntimeTypeModel.Default[typeof(MyType)][1].SupportNull = true; RuntimeTypeModel.Default[typeof(MyEmbeddedType)][2].SupportNull = true; RuntimeTypeModel.Default[typeof(MySubType)][3].SupportNull = true; 我会得到零散的空引用错误。奇怪的是,这似乎取决于所选择的协议索引的值和顺序。 - BaltoStar
RuntimeTypeModel.Default... 方法对我很有效。如果能在 protomember 属性中使用就更好了。 - MineR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接