Protobuf.net - class 的所有字段都需要序列化
Protobuf.net - all fields of a class required to be serialized
这里有一篇example class provided by Marc Gravel介绍他如何使用Protobuf.net:
[ProtoContract]
class Person {
[ProtoMember(1)]
public int Id {get;set;}
[ProtoMember(2)]
public string Name {get;set;}
[ProtoMember(3)]
public Address Address {get;set;}
}
[ProtoContract]
class Address {
[ProtoMember(1)]
public string Line1 {get;set;}
[ProtoMember(2)]
public string Line2 {get;set;}
}
我有一些问题,在网上搜索后找不到答案:
如果在第 1 天,我知道我不需要名称 属性 [ProtoMember(2)],那么如果我省略 [ProtoMember(2)] 属性, Protobut.net 会忽略 属性 并且不将其包含在输出序列化数据中吗?如果为真,那么当数据在另一端被反序列化时 - Name 初始化为什么 - null?
假设所有 3 个属性最初都已序列化,如上所示。如果将来发现不再需要名称 属性 [ProtoMember(2)],是否可以安全地省略 [ProtoMember(2)] 属性,以便只有第一个和第三个 属性 连载了吗?如果为真,是否可以简单地保留所示的属性编号(即 1 和 3)?如果是这种情况,有什么注意事项吗?
如果可以在 class 中省略 属性 的序列化属性,那么如果反序列化端的 class 定义是不同步?比如反序列化class定义了上面所有的3个属性,但是序列化代码只定义了1和3?同样,如果反序列化代码只希望看到属性 1 和 3,但序列化代码发送所有 3,这仍然有效还是会产生错误?
- 正确;就初始化为什么而言——这通常取决于你的类型,所以 在这种情况下 (因为你的类型没有初始化它),是的:
null
(注意: 还有一个选项可以抑制构造函数,在这种情况下它将是 null
即使 你的 class 有一个构造函数/初始化器)
- 是的,这很好(并且符合预期)
- 属性的添加和删除是正常的,所以图书馆可以原谅;当收到意外字段时,接下来会发生什么取决于您的 class 是否实现了
IExtensible
(通常是通过子classing Extensible
)——如果是这样,意外数据将被存储分开,这样它仍然可以手动查询,或者(更常见)"round tripped"(即如果你再次序列化它,额外的数据会被持久化,即使你没有预料到它)
- (是的,我知道你没有问 4)- 该库还支持 "conditional serialization",可以根据条件省略 where-by 属性 - 例如
public bool ShouldSerializeName() => Name != null && SomethingElse == 42;
由于其他人可能想知道这些问题的答案,所以我决定 post 这个问题并分享我的发现。
这些问题实际上很容易用测试程序解决:
class Program
{
static void Main(string[] args)
{
var person = new Person1
{
Id = 12345,
Name = "Fred",
Address = new Address
{
Line1 = "Flat 1",
Line2 = "The Meadows"
}
};
//
byte[] arr = Serialize(person);
Person2 newPerson = Deserialize(arr);
/*
using (var file = File.Create("person.bin"))
{
Serializer.Serialize(file, person);
}
//
Person newPerson;
using (var file = File.OpenRead("person.bin"))
{
newPerson = Serializer.Deserialize<Person>(file);
}
*/
}
public static byte[] Serialize(Person1 person)
{
byte[] result;
using (var stream = new MemoryStream())
{
Serializer.Serialize(stream, person);
result = stream.ToArray();
}
return result;
}
public static Person2 Deserialize(byte[] tData)
{
using (var ms = new MemoryStream(tData))
{
return Serializer.Deserialize<Person2>(ms);
}
}
}
[ProtoContract]
class Person1
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
}
[ProtoContract]
class Address
{
[ProtoMember(1)]
public string Line1 { get; set; }
[ProtoMember(2)]
public string Line2 { get; set; }
}
[ProtoContract]
class Person2
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
}
因此,要尝试在序列化时 Protobuf.net 是否会忽略名称 属性,首先 运行 调试代码以查看所有 3 个属性序列化的总字节数.这可以通过在 Serialize(person) 行设置断点并检查 arr.
的大小来完成
然后,从 Person1 class 的名称 属性 中删除 [ProtoMember(2)] 属性。 运行 debug 中的代码显示它省略了它,因为字节数比以前少了 6 个。然后将对象反序列化回 Person2 的对象时,它会显示 Name 属性 被初始化为 null。
替换 Person1 class 中的 Name 属性 的 [ProtoMember(2)] 属性后,删除 Person2 class 的相同属性。通过代码调试后,显示Deserialize调用后,Person2.Name 属性设置为null。
因此,看起来 Protobuf.net 设计得非常灵活、高效,并且在某些方面是向后兼容的,因为它支持删除过时的属性。
这里有一篇example class provided by Marc Gravel介绍他如何使用Protobuf.net:
[ProtoContract]
class Person {
[ProtoMember(1)]
public int Id {get;set;}
[ProtoMember(2)]
public string Name {get;set;}
[ProtoMember(3)]
public Address Address {get;set;}
}
[ProtoContract]
class Address {
[ProtoMember(1)]
public string Line1 {get;set;}
[ProtoMember(2)]
public string Line2 {get;set;}
}
我有一些问题,在网上搜索后找不到答案:
如果在第 1 天,我知道我不需要名称 属性 [ProtoMember(2)],那么如果我省略 [ProtoMember(2)] 属性, Protobut.net 会忽略 属性 并且不将其包含在输出序列化数据中吗?如果为真,那么当数据在另一端被反序列化时 - Name 初始化为什么 - null?
假设所有 3 个属性最初都已序列化,如上所示。如果将来发现不再需要名称 属性 [ProtoMember(2)],是否可以安全地省略 [ProtoMember(2)] 属性,以便只有第一个和第三个 属性 连载了吗?如果为真,是否可以简单地保留所示的属性编号(即 1 和 3)?如果是这种情况,有什么注意事项吗?
如果可以在 class 中省略 属性 的序列化属性,那么如果反序列化端的 class 定义是不同步?比如反序列化class定义了上面所有的3个属性,但是序列化代码只定义了1和3?同样,如果反序列化代码只希望看到属性 1 和 3,但序列化代码发送所有 3,这仍然有效还是会产生错误?
- 正确;就初始化为什么而言——这通常取决于你的类型,所以 在这种情况下 (因为你的类型没有初始化它),是的:
null
(注意: 还有一个选项可以抑制构造函数,在这种情况下它将是null
即使 你的 class 有一个构造函数/初始化器) - 是的,这很好(并且符合预期)
- 属性的添加和删除是正常的,所以图书馆可以原谅;当收到意外字段时,接下来会发生什么取决于您的 class 是否实现了
IExtensible
(通常是通过子classingExtensible
)——如果是这样,意外数据将被存储分开,这样它仍然可以手动查询,或者(更常见)"round tripped"(即如果你再次序列化它,额外的数据会被持久化,即使你没有预料到它) - (是的,我知道你没有问 4)- 该库还支持 "conditional serialization",可以根据条件省略 where-by 属性 - 例如
public bool ShouldSerializeName() => Name != null && SomethingElse == 42;
由于其他人可能想知道这些问题的答案,所以我决定 post 这个问题并分享我的发现。
这些问题实际上很容易用测试程序解决:
class Program
{
static void Main(string[] args)
{
var person = new Person1
{
Id = 12345,
Name = "Fred",
Address = new Address
{
Line1 = "Flat 1",
Line2 = "The Meadows"
}
};
//
byte[] arr = Serialize(person);
Person2 newPerson = Deserialize(arr);
/*
using (var file = File.Create("person.bin"))
{
Serializer.Serialize(file, person);
}
//
Person newPerson;
using (var file = File.OpenRead("person.bin"))
{
newPerson = Serializer.Deserialize<Person>(file);
}
*/
}
public static byte[] Serialize(Person1 person)
{
byte[] result;
using (var stream = new MemoryStream())
{
Serializer.Serialize(stream, person);
result = stream.ToArray();
}
return result;
}
public static Person2 Deserialize(byte[] tData)
{
using (var ms = new MemoryStream(tData))
{
return Serializer.Deserialize<Person2>(ms);
}
}
}
[ProtoContract]
class Person1
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
}
[ProtoContract]
class Address
{
[ProtoMember(1)]
public string Line1 { get; set; }
[ProtoMember(2)]
public string Line2 { get; set; }
}
[ProtoContract]
class Person2
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
}
因此,要尝试在序列化时 Protobuf.net 是否会忽略名称 属性,首先 运行 调试代码以查看所有 3 个属性序列化的总字节数.这可以通过在 Serialize(person) 行设置断点并检查 arr.
的大小来完成然后,从 Person1 class 的名称 属性 中删除 [ProtoMember(2)] 属性。 运行 debug 中的代码显示它省略了它,因为字节数比以前少了 6 个。然后将对象反序列化回 Person2 的对象时,它会显示 Name 属性 被初始化为 null。
替换 Person1 class 中的 Name 属性 的 [ProtoMember(2)] 属性后,删除 Person2 class 的相同属性。通过代码调试后,显示Deserialize调用后,Person2.Name 属性设置为null。
因此,看起来 Protobuf.net 设计得非常灵活、高效,并且在某些方面是向后兼容的,因为它支持删除过时的属性。