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 为 null,protobuf-net
序列化程序将从序列化字节中省略该元素。
在电线的另一边,发生了一些非常有趣的事情:
protobuf-net
反序列化程序读取字节,了解需要 3 个元素,但仅查找元素索引 0 和 2 的字节。 因此,它将元素索引 1 创建为MyType
的空实例。
所以反序列化列表等效于:
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 的元素可能会对处理代码产生严重的不利后果 - 处理代码理所当然地不应该意识到也不关心 serdes 细节。
有解决方法吗?
基本上,不,没有解决方法,并且在protobuf-net中不支持这种情况。底层协议缓冲区格式(protobuf-net 实现)没有 null 的概念;我无法在常规格式中表示空实例。类列表只是长度前缀节点的重复块,因此(以密集的二进制格式,而不是文本):
[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()
;零长度在协议缓冲区中是完全有效和明确定义的。
与其他一些格式不同,我实际上没有任何地方可以添加额外的元数据来表示"我是空"(例如,在 xml 中@xsi:nil
)。
我可能会将其破解到"保留引用"代码中,但我已经检查过,目前没有对 null 的支持 - 我接受这有点疏忽,但我还要强调"保留引用"代码不是标准的 protobuf,任何其他库(java、golang 等)可能难以使用该配置;我只会主张,如果你知道你只是在谈论protobuf-net和protobuf-net。当然,空支持目前不存在,需要黑客攻击:)