用ProtoBuf序列化.. NET不标记成员
本文关键字:成员 NET ProtoBuf 序列化 | 更新日期: 2023-09-27 18:10:11
我在某处读到ProtoBuf作者的评论。净:
有自动推断数字的选项,但这是脆弱的,不推荐。只有当你知道你永远不需要添加更多成员时才使用这个(它按字母顺序排列,所以添加一个新的AardvarkCount会破坏一切)。
这正是我感兴趣的情况:)
我有一个类似于map-reduce的场景,我想序列化使用协议缓冲区在远程机器上生成的结果(例如map-reduce的"map"端),然后读取它们并合并这些结果以进行进一步处理(例如"reduce"端)。
我不想在每个可能的类上开始一个属性装饰马拉松,我有可能在这个过程中被序列化,我确实发现协议缓冲区非常诱人,因为我可以用Mono创建结果,并在MS.NET上毫不费力地消耗它们,反之亦然…
不预先标记成员的明显缺点并没有困扰我,因为完全相同的软件修订会产生/消费,所以我不需要担心新成员在代码中弹出并扰乱我的整个方案…
简而言之,我的问题是:
- 我怎么做(序列化与ProtoBuf。. NET没有标记/构建我自己的元类)?
- 我的计划中有什么漏洞是我明显错过的吗?
如果您可以使用单个属性,那么技巧是:
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class WithImplicitFields
{
public int X { get; set; }
public string Y { get; set; }
}
这里有2个选项;AllPublic
的工作原理类似于XmlSerializer
——公共属性和字段被序列化(使用字母顺序选择标签编号);AllFields
的工作方式有点像BinaryFormatter
——字段是序列化的(还是按字母顺序)。
我不记得这是否在v2 API上可用;我知道这是在我的工作清单上,以确保工作!但是如果你想在v2中没有任何属性,我相信我可以添加一个Add(ImplicitFields)
过载。
只要两端不不同步,就可以。如果您存储数据,或者不"同步"对两端进行版本化,则可能会出现问题。另请参见enum上的智能感知注释(它几乎重复了您已经意识到的警告)。
我有同样的问题,这就是我如何用TypeModel解决它。它基于按名称排序的属性(但是它不检查给定类型的属性setter/getter是否存在或序列化能力):
[TestFixture]
public class InferredProtoPoc
{
[Test]
public void UsageTest()
{
var model = TypeModel.Create();
// Dynamically create the model for MyPoco
AddProperties(model, typeof(MyPoco));
// Display the Generated Schema of MyPoco
Console.WriteLine(model.GetSchema(typeof(MyPoco)));
var instance = new MyPoco
{
IntegerProperty = 42,
StringProperty = "Foobar",
Containers = new List<EmbeddedPoco>
{
new EmbeddedPoco { Id = 12, Name = "MyFirstOne" },
new EmbeddedPoco { Id = 13, Name = "EmbeddedAgain" }
}
};
var ms = new MemoryStream();
model.Serialize(ms, instance);
ms.Seek(0, SeekOrigin.Begin);
var res = (MyPoco) model.Deserialize(ms, null, typeof(MyPoco));
Assert.IsNotNull(res);
Assert.AreEqual(42, res.IntegerProperty);
Assert.AreEqual("Foobar", res.StringProperty);
var list = res.Containers;
Assert.IsNotNull(list);
Assert.AreEqual(2, list.Count);
Assert.IsTrue(list.Any(x => x.Id == 12));
Assert.IsTrue(list.Where(x => x.Id == 12).Any(x => x.Name == "MyFirstOne"));
Assert.IsTrue(list.Any(x => x.Id == 13));
Assert.IsTrue(list.Where(x => x.Id == 13).Any(x => x.Name == "EmbeddedAgain"));
}
private static void AddProperties(RuntimeTypeModel model, Type type)
{
var metaType = model.Add(type, true);
foreach (var property in type.GetProperties().OrderBy(x => x.Name))
{
metaType.Add(property.Name);
var propertyType = property.PropertyType;
if (!IsBuiltinType(propertyType) &&
!model.IsDefined(propertyType) &&
propertyType.GetProperties().Length > 0)
{
AddProperties(model, propertyType);
}
}
}
private static bool IsBuiltinType(Type type)
{
return type.IsValueType || type == typeof (string);
}
}
public class MyPoco
{
public int IntegerProperty { get; set; }
public string StringProperty { get; set; }
public List<EmbeddedPoco> Containers { get; set; }
}
public class EmbeddedPoco
{
public int Id { get; set; }
public String Name { get; set; }
}
这就是你运行它得到的结果。
message EmbeddedPoco {
optional int32 Id = 1;
optional string Name = 2;
}
message MyPoco {
repeated EmbeddedPoco Containers = 1;
optional int32 IntegerProperty = 2;
optional string StringProperty = 3;
}
为了提高性能,您可以选择编译TypeModel,和/或存储生成的原型以供将来使用。但是要注意,如果poco(普通的旧容器对象)演变,那么对协议缓冲区的隐藏依赖从长远来看可能是危险的。