ProtoBuf-net 序列化包含列表的对象时的无效行为
ProtoBuf-net invalid behaviour when serializing object containing list
我很难解释以下示例中的行为:
[ProtoContract]
public class Class1Proto
{
[ProtoMember(1)]
public int data1 = 1;
[ProtoMember(2)]
public string data2 = "MYRANDOMSTRING";
}
[ProtoContract]
public class ProtoChunk
{
[ProtoMember(1)]
public List<Class1Proto> arr = new List<Class1Proto>();
public const int PageSize = 4096;
}
用法:
byte[] page = new byte[ProtoChunk.PageSize];
ProtoChunk originalData = new ProtoChunk();
for (int i = 0; i < 100; i++)
{
Class1Proto p = new Class1Proto();
p.data1 = i * 2;
p.data2 = (i * 2).ToString();
originalData.arr.Add(p);
}
using (var memStream = new MemoryStream(page, writable:true))
{
Serializer.SerializeWithLengthPrefix(memStream, originalData, PrefixStyle.Fixed32);
}
using (var memStream = new MemoryStream(page, writable:false))
{
ProtoChunk deserializedData = Serializer.DeserializeWithLengthPrefix<ProtoChunk>(memStream, PrefixStyle.Fixed32);
}
我的期望是 originalData
和 deserializedData
应该相同。他们大多是 deserializedData.arr[0].data1 == 1 while originalData.arr[0].data1 == 0
。所有其他对象都是相同的,甚至包括 originalData.arr[0].data2 and deserializedData.arr[0].data2
(字符串字段)。
protobuf-net 假定 "implicit zero defaults" - 即除非另有说明,否则成员的默认值为零,这意味着:不传输零。这不是纯粹的任意 - 这实际上是 "proto3" 规范(好吧......或多或少;在 "proto3" 中,零是 only 允许的默认值) .
你的代码——尤其是 属性 初始化器——就像它有一个默认值 1 一样,所以:当零没有被传输时,1 仍然被你的构造函数应用,这就变成了值(protobuf 中的反序列化是一个 "merge" 操作 - 保留预先存在的值,再次符合规范)。
选项:
- 告诉 protobuf-net 您的默认设置 - 添加
[DefaultValue(1)]
到 属性
- 告诉 protobuf-net 不要 运行 构造函数(和 属性 初始化器)- 添加
SkipConstructor = true
到 [ProtoContract]
- 告诉 protobuf-net 不要假设这种行为:
RuntimeTypeModel.Default.ImplicitZeroDefault = false;
- 添加你自己的条件序列化回调(如果你真的想要我可以举个例子)
我个人会使用第一个选项。
我很难解释以下示例中的行为:
[ProtoContract]
public class Class1Proto
{
[ProtoMember(1)]
public int data1 = 1;
[ProtoMember(2)]
public string data2 = "MYRANDOMSTRING";
}
[ProtoContract]
public class ProtoChunk
{
[ProtoMember(1)]
public List<Class1Proto> arr = new List<Class1Proto>();
public const int PageSize = 4096;
}
用法:
byte[] page = new byte[ProtoChunk.PageSize];
ProtoChunk originalData = new ProtoChunk();
for (int i = 0; i < 100; i++)
{
Class1Proto p = new Class1Proto();
p.data1 = i * 2;
p.data2 = (i * 2).ToString();
originalData.arr.Add(p);
}
using (var memStream = new MemoryStream(page, writable:true))
{
Serializer.SerializeWithLengthPrefix(memStream, originalData, PrefixStyle.Fixed32);
}
using (var memStream = new MemoryStream(page, writable:false))
{
ProtoChunk deserializedData = Serializer.DeserializeWithLengthPrefix<ProtoChunk>(memStream, PrefixStyle.Fixed32);
}
我的期望是 originalData
和 deserializedData
应该相同。他们大多是 deserializedData.arr[0].data1 == 1 while originalData.arr[0].data1 == 0
。所有其他对象都是相同的,甚至包括 originalData.arr[0].data2 and deserializedData.arr[0].data2
(字符串字段)。
protobuf-net 假定 "implicit zero defaults" - 即除非另有说明,否则成员的默认值为零,这意味着:不传输零。这不是纯粹的任意 - 这实际上是 "proto3" 规范(好吧......或多或少;在 "proto3" 中,零是 only 允许的默认值) .
你的代码——尤其是 属性 初始化器——就像它有一个默认值 1 一样,所以:当零没有被传输时,1 仍然被你的构造函数应用,这就变成了值(protobuf 中的反序列化是一个 "merge" 操作 - 保留预先存在的值,再次符合规范)。
选项:
- 告诉 protobuf-net 您的默认设置 - 添加
[DefaultValue(1)]
到 属性 - 告诉 protobuf-net 不要 运行 构造函数(和 属性 初始化器)- 添加
SkipConstructor = true
到[ProtoContract]
- 告诉 protobuf-net 不要假设这种行为:
RuntimeTypeModel.Default.ImplicitZeroDefault = false;
- 添加你自己的条件序列化回调(如果你真的想要我可以举个例子)
我个人会使用第一个选项。