协议缓冲区是否支持序列化具有共享引用的对象图?

7
请看下面这个简单的程序(基于protobuf-net项目v1维基上的示例):
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using ProtoBuf;

namespace HelloProtoBuf
{
  [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; }
  }


  class Program
  {
    static void Main(string[] args)
    {
      var person = new Person
      {
        Id = 12345,
        Name = "Fred",
        Address = new Address
        {
          Line1 = "Flat 1",
          Line2 = "The Meadows"
        }
      };
      var person2 = new Person
      {
        Id = 4553,
        Name = "Nadya",
        Address = person.Address
      };
      var persons = new List<Person> { person, person2 };
      Debug.Assert(ReferenceEquals(persons[0].Address, persons[1].Address));

      using (var file = File.Create("persons.bin"))
      {
        Serializer.Serialize(file, persons);
      }
      List<Person> persons2;
      using (var file = File.OpenRead("persons.bin"))
      {
        persons2 = Serializer.Deserialize<List<Person>>(file);
      }
      Debug.Assert(ReferenceEquals(persons2[0].Address, persons2[1].Address));
    }
  }
}

第二个断言失败了。这是protobuf-net实现中的一个bug还是协议缓冲区根本不支持具有共享引用的对象图?谢谢。
1个回答

9

Protocol Buffers本身不支持此功能,因此不是错误。实际上,XmlSerializer和DataContractSerializer*也会执行相同的操作(可能JavaScriptSerializer和JSON.NET也一样)。

但是,这是一个常见的请求,因此在protobuf-net v2中支持此功能(基本上:我作弊了)。只需将其更改为:

    [ProtoMember(3, AsReference=true)]
    public Address Address { get; set; }

(并使用我将在约5分钟内上传的v2 dll,或从代码构建)


*=注意: DataContractSerializer支持引用,但只有在使用特定构造函数时才支持;默认情况下禁用


1
能否通过命令式模型(RuntimeTypeModel)传达这些信息?问题是我的代理被赋予了DataContract/DataMember属性,以避免引用protobuf-net。谢谢。 - mark
1
@mark - 是的;yourMetaType[3].AsReference = true;,其中 3 是字段编号(在 DCS 术语中为 Order=3)。 - Marc Gravell
马克,这个图形序列化在幕后是如何工作的?你是为每个引用对象创建隐藏的int id(就像代理主键)然后在加载时恢复它们吗? - Bryan Legend
1
@Lone 是的,完全就是那样 - 只针对标记为此用途的关系。 - Marc Gravell
太棒了。我打算在这个周末的Ludem Dare比赛中使用它作为一个快速而简单的序列化器(http://www.ludumdare.com/compo/rules/)。需要像你做的这个一样很多很棒的忍者工具! - Bryan Legend
@Lone 在你出发前确保你熟悉它 - 我不想因为让你卡在某个地方而拖慢你的速度。祝好运! - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接