次のクラスを使用して個々のオブジェクト (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 プレフィックスメソッドを機能させることができれば、それも良いでしょう。