用ProtoBuf序列化.. NET不标记成员

本文关键字:成员 NET ProtoBuf 序列化 | 更新日期: 2023-09-27 18:10:11

我在某处读到ProtoBuf作者的评论。净:

有自动推断数字的选项,但这是脆弱的,不推荐。只有当你知道你永远不需要添加更多成员时才使用这个(它按字母顺序排列,所以添加一个新的AardvarkCount会破坏一切)。

这正是我感兴趣的情况:)

我有一个类似于map-reduce的场景,我想序列化使用协议缓冲区在远程机器上生成的结果(例如map-reduce的"map"端),然后读取它们并合并这些结果以进行进一步处理(例如"reduce"端)。

我不想在每个可能的类上开始一个属性装饰马拉松,我有可能在这个过程中被序列化,我确实发现协议缓冲区非常诱人,因为我可以用Mono创建结果,并在MS.NET上毫不费力地消耗它们,反之亦然…

不预先标记成员的明显缺点并没有困扰我,因为完全相同的软件修订会产生/消费,所以我不需要担心新成员在代码中弹出并扰乱我的整个方案…

简而言之,我的问题是:

  • 我怎么做(序列化与ProtoBuf。. 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(普通的旧容器对象)演变,那么对协议缓冲区的隐藏依赖从长远来看可能是危险的。