5

オブジェクト グラフをシリアル化しようとすると例外が発生します (あまり深くはありません)。その意味のある部分は次のようになります。

[エラー] 致命的な未処理の例外: ProtoBuf.ProtoException: 再帰の可能性が検出されました (オフセット: 5 レベル): ProtoBuf.ProtoWriter.CheckRecursionStackAndPush で赤 (オブジェクト) <0x00127> で ProtoBuf.ProtoWriter.StartSubItem (オブジェクト,ProtoBuf.ProtoWriter) ,ブール) <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 v2 ( rev 491 )からコンパイルされたものです。

実際には、protobuf-net コードを修正することで解決しました。の値ProtoBuf.ProtoWriter.RecursionCheckDepthを 100 に変更したところ、すべて問題ないようです。

問題は、protobuf コードを変更せずにそのような種類のグラフをシリアル化する「真の」方法があるかどうかです。そのような動作は正しいですか、それともバグですか?

私のプラットフォームは、Windows 7 Professional 64 ビットの Mono-2.10-8 です。

PS また、次のコードで deserizlie を実行する場合、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

この例外は、データ内に同じ参照が見られた場合 (同じパスで 2 回) にのみスローされ、追跡は深さが少なくとも である場合にのみ有効RecursionCheckDepthになります。これは、引用されている 10 ~ 15 の深さの制限をすぐに疑いますが、protobufがカウントしているのとまったく同じレベルを処理するとは限りません。この数を 100 に上げても機能するというのは、私には意味がありません。実際、これRecursionCheckDepthが存在するのは、「典型的な」グラフに関連する労力を制限するための純粋な最適化であり、開始した場合にのみ、より厳密なチェックを可能にします。深く見ること。

ただし、これは継承ベースの処理の微妙なバグも示唆している可能性があることに注意してAsReferenceください。私は protobuf-net を広範かつ継続的に使用していますが、そのような問題は見たことがありません。再現可能なレプロをお持ちの場合は、ぜひご覧ください。

于 2012-04-09T08:50:49.237 に答える