1

次のクラスを使用して個々のオブジェクト (T) をプレフィックスでシリアル化し、DeserializeItems() メソッドを使用してリストに逆シリアル化しようとしています。

public class ProtobufSerializationProvider<T> : ISerializationProvider<T>
{
    readonly Type type; 
    readonly TypeModel model;

    public ProtobufSerializationProvider()
    {
        this.type = typeof(T); 
        this.model = createModel(this.type);
    }

    public byte[] Serialize(T instance)
    {
        byte[] buffer; 
        using (MemoryStream strm = new MemoryStream())
        {
            model.SerializeWithLengthPrefix
              (strm, instance, type, PrefixStyle.Base128, 1);

            buffer = strm.GetBuffer();              
        }
        return buffer; 
    }

    // here is the problem method
    public IEnumerable<T> DeserializeAll(MemoryStream stream)
    {
        return model.DeserializeItems<T>(stream, PrefixStyle.Base128, 1); 
    }

    TypeModel createModel(Type type)
    {
        try
        {
            RuntimeTypeModel runtimeModel = TypeModel.Create();
            this.addTypeToModel(runtimeModel, type);
            this.addTypePropertiesToModel(runtimeModel, type);
            return runtimeModel.Compile();
        }
        catch (Exception e)
        {
            throw e.InnerException;
        }
    }

    void addTypePropertiesToModel(RuntimeTypeModel typeModel, Type type)
    {
        PropertyInfo[] properties = type.GetProperties();
        for (int i = 0; i < properties.Length; i++)
        {
            Type innerType = properties[i].PropertyType;
            if (!innerType.IsPrimitive && !(innerType == typeof(string)))
            {
                addTypeToModel(typeModel, properties[i].PropertyType);
            }
        }
    }

    MetaType addTypeToModel(RuntimeTypeModel typeModel, Type t)
    {                
        var properties = t.GetProperties()
            .Select(p => p.Name)
            .OrderBy(name => name);

        return typeModel
            .Add(t, true)
            .Add(properties.ToArray());            
    }
}

ToList() をキャストするか、Count() をカウントするなどして IEnumerable を列挙しようとすると、デフォルトの InvalidOperationException 「オブジェクトの現在の状態が原因で操作が有効ではありません」が発生します。具体的には MoveNext() メソッドエラーがスローされます:

enumerator.MoveNext()

また、ストリームは DeserializeItems(stream) が戻るまで開いている必要があり、そうであることを確認しました。しかし、IEnumerable が正常に返されると、使用できなくなります。

連載品に問題があるのか​​不明です。また、各アイテムが 256 バイトであることに気付きましたが、これらのバイトの大部分は末尾の null です。

これは、テストとしてシリアル化しているクラスです。モデルを手動で作成するため、属性を使用していないことに注意してください。

public class NestedFoo
{
    public string NestedFooStr { get; set; } 
}

public class Foo
{
    public string Foo1 { get; set; }
    public string Foo2 { get; set; }
    public string Foo3 { get; set; }
    public string Foo4 { get; set; }

    public NestedFoo NestedFoo { get; set; }
}

ありがとう。

アップデート

MemoryStream.GetBuffer() から MemoryStream.ToArray() に切り替えた後、メッセージは短縮されますが、IEnumerable .ToList() またはその他の操作をキャストしようとすると、同じエラーが発生します。

ただし、.ToArray() に切り替えた後、列挙後に IEnumerable の 'Current' プロパティがエラーをスローする前に IEnumerable の最後の要素に到達するのに対し、.GetBuffer() を使用すると最初の 'Current' プロパティが使用されることに気付きました。エラーがスローされたときの要素。

問題は、IEnumerable<> がストリームの最後に達したため、いつ要素がなくなったのかわからないことではないでしょうか? SerializeWithLengthPrefix() でシリアル化されたいくつかの項目を単一の byte[] に追加すると、配列の末尾に null が残る場合があります。その配列全体を DeserializeItems メソッドに読み込む場合、終了方法を知る必要がありますか、それとも長さのプレフィックスを検出した場合にのみ続行しますか (期待どおりですか?)。

また、私が持っていたもう 1 つの質問は、SerializeWithLengthPrefix() メソッドを使用することは、次のように、DataFormat.Group 列挙を使用して 1 回の操作でリストをシリアル化することとどの程度同等であるかということでした。

[ProtoContract]
public class ProtoList<T>
{
    [ProtoMember(1, DataFormat = DataFormat.Group)]
    public List<T> List { get; set; }

    public ProtoList() { }

    public ProtoList(IEnumerable<T> items)
    {
        this.List = new List<T>(items); 
    }
}

このようなコンテナーを使用すると非常にうまく機能するように見えるので、リストを 1 ショットでシリアル化したい場合は、このルートを使用することができます。

ただし、アイテムが追加されたときに個別にシリアル化するために、現在独自のプレフィックス関数を使用しているため、Protobuf プレフィックスメソッドを機能させることができれば、それも良いでしょう。

4

2 に答える 2

2

256/末尾のヌルは、を使用しているためですMemoryStream.GetBuffer()。このメソッドは、バッファが大きすぎるため、 と組み合わせて使用​​する限り問題ありません。Length適切なサイズの配列が必要な場合は、ToArray()代わりに使用してください。ArraySegment<byte>または、データのコピーを避けるために使用します。

これにより、例外も修正される可能性があります (0 はフィールド ヘッダーの有効な値ではありません)。そうでない場合は、コメントを残してください。詳細を確認する必要があります。修正された場合は、私にもお知らせください。エラー メッセージ (フィールド ヘッダーが 0 の場合) がより役立つように努めます。

于 2012-04-17T06:58:47.367 に答える
1

確認のために、同じ問題に遭遇しました。

タグ/パケット ID を 0 として使用しないでください。すべて解決しました。

于 2015-07-18T09:42:01.707 に答える