5

我在尝试序列化对象图时遇到异常(不是很深)。其中有意义的部分是这样的:

[错误] 致命的未处理异常:ProtoBuf.ProtoException:检测到可能的递归(偏移量:5 级):ProtoBuf.ProtoWriter.CheckRecursionStackAndPush(对象)<0x00127> 在 ProtoBuf.ProtoWriter.StartSubItem(对象,ProtoBuf.ProtoWriter ,bool) <0x0002f>

该图表示文件/目录结构,我的模型(简化)如下所示:

[ProtoContract] 
[ProtoInclude(100, typeof(PackageDirectory))]
[ProtoInclude(200, typeof(PackageFile))]
public abstract class PackageMember
{
   [ProtoMember(1)] 
   public virtual string Name { get; protected set; }

   [ProtoMember(2, AsReference=true)] 
   public PackageDirectory ParentDirectory { get; protected set; }  
}

[ProtoContract]
public class PackageDirectory : PackageMember
{
   [ProtoMember(3)]
   private Dictionary<string, PackageMember> _children;

   public PackageDirectory()
   {
      _children = new Dictionary<string, PackageMember>();
   }

   public PackageDirectory (string name, PackageDirectory parentDirectory)
      : this()
   {
      this.ParentDirectory = parentDirectory;
      this.Name = name;         
   }

   public void Add (PackageMember member)
   {
      _children.Add(member.Name, member);
   }
}

[ProtoContract]
public class PackageFile : PackageMember
{
   private Stream _file;
   private BinaryReader _reader;

   private PackageFile() 
   {}

   public PackageFile (string name, int offset, int length, PackageDirectory directory,  Stream file)
   {
      this.Name = name;
      this.Length = length;
      this.Offset = offset;
      this.ParentDirectory = directory;

      _file = file;
      _reader = new BinaryReader(_file);
   }

   [OnDeserialized]
   protected virtual void OnDeserialized(SerializationContext context)
   {
      var deserializationContext = context.Context as DeserializationContext;

      if (deserializationContext != null)
      { 
         _file = deserializationContext.FileStream;
         _reader = new BinaryReader(_file);
      }
   }

   [ProtoMember(3)]
   public int Offset { get; private set; }

   [ProtoMember(4)]
   public int Length { get; private set; }
}

这棵树的深度接近 10-15 级,小于ProtoBuf.ProtoWriter.RecursionCheckDepth值 (25)。(所以也许这是一个错误?)使用的 protobuf-net 版本是从trunk v2rev 491)编译的版本。

实际上,我通过修改 protobuf-net 代码解决了这个问题。我将值更改ProtoBuf.ProtoWriter.RecursionCheckDepth为 100,一切似乎都很好。

问题是是否有任何“真正的”方法可以在不修改 protobuf 代码的情况下序列化此类图形?这种行为是正确的还是错误的?

我的平台是Windows 7 Professional 64 位上的 Mono-2.10-8

PS 另外我发现,如果我使用以下代码进行解串,我应该将 PackageDirectory 无参数构造函数公开。

var value = new PackageDirectory();
RuntimeTypeModel.Default.Deserialize(ms, value, typeof(PackageDirectory), new SerializationContext {
   Context = new DeserializationContext {
   FileStream = _file,
}});

这是另一个主题,但已通过提供的代码很好地说明了这一点。我认为在这种情况下应该允许声明私有构造函数,因为现在行为与 Serializer.Deserialize(...) 的行为不同。

4

1 回答 1

6

仅当在数据中看到相同的引用(在同一路径中出现两次)时才会引发此异常,并且仅在深度至少为时才启用RecursionCheckDepth跟踪。这立即让我怀疑引用的 10-15 深度限制,尽管 protobuf 处理的级别不一定与您计算的级别完全相同对我来说,将这个数字提高到 100 就可以让它工作是没有意义的——事实上,它的存在RecursionCheckDepth纯粹是一种优化,以限制“典型”图表所涉及的工作量,只有在它开始时才能够进行更严格的检查看得很深。

但是,我注意到这也可能暗示基于继承的处理中存在一些微妙的错误,可能也与AsReference. 我广泛且不断地使用protobuf-net,我还没有看到这样的问题。如果您有可重现的复制品,我非常希望看到它。

于 2012-04-09T08:50:49.237 回答