我正在序列化许多对象并将其写入流,然后使用Protobuf.net从流中反序列化这些对象。
对象类型是在运行时确定的,所以我必须使用NonGeneric方法"TryDeserializeWithLengthPrefix"。
我一直收到"源数据中的无效字段:0"异常。但是当我使用通用方法"DeserializeWithLengthPrefix()"时,它工作得很好。
非常奇怪的是,一开始我得到的是"源数据中的无效字段:0",但当我更改数组长度时,我开始得到"System.InvalidOperationException"!第一个异常仅在长度为5或9时发生。我试过用一个类代替int[],但结果是一样的。
下面是测试代码,非常感谢你的帮助。
MemoryStream outputStream = new MemoryStream();
MemoryStream inputStream;
for (int i = 0; i < 10; i++)
{
//an int array as the object to serialize
var data = new int[] { 1, 2, 3, 4, 5 };
Serializer.SerializeWithLengthPrefix(outputStream, data, PrefixStyle.Base128);
}
var dataBytes = outputStream.ToArray();
inputStream = new MemoryStream(dataBytes);
while (inputStream.Position != inputStream.Length)
{
object output;
//not working, "System.InvalidOperationException" or "Invalid field in source data: 0" depend on the lenth of the array
Serializer.NonGeneric.TryDeserializeWithLengthPrefix(inputStream, PrefixStyle.Base128, t => typeof(int[]), out output);
//working! every time
var output = Serializer.DeserializeWithLengthPrefix<int[]>(inputStream, PrefixStyle.Base128);
foreach (var num in (int[])output)
{
Console.WriteLine(num);
}
}
由于有一个可选的参数,它们是略有不同的场景。您使用的SerializeWithLengthPrefix
和DeserializeWithLengthPrefix
方法有一个可选参数,如果省略,则默认为零。这意味着您正在使用零进行序列化,但没有字段号(零字段号在protobuf中永远不合法)。有些人需要这样。还有另一个重载,允许您指定字段号以及长度前缀。
您正在使用的TryDeserializeWithLengthPrefix
的重载旨在用于第二种情况——特别是,您与委托一起使用的版本旨在允许您告诉序列化程序,它应该根据字段号考虑的类型(在您的情况下,t => typeof(int[])
将为每个标记t
返回相同的内容)。如果您打算使用该版本进行反序列化,则在序列化时必须包含字段号,例如:
Serializer.SerializeWithLengthPrefix(
outputStream, data, PrefixStyle.Base128, 1);
和
var output = Serializer.DeserializeWithLengthPrefix<int[]>(
inputStream, PrefixStyle.Base128, 1);
这应该也与行正确工作:
Serializer.NonGeneric.TryDeserializeWithLengthPrefix(
inputStream, PrefixStyle.Base128, t => typeof(int[]), out output);
(您应该会发现t
就是1
)。
或者,如果不希望在数据中使用字段号,可以使用TryReadLengthPrefix(...)
来获取长度,然后创建ProtoReader
来指定确切的长度。
附带说明:通常应避免在Stream
中检查.Length
与.Position
,因为.Length
通常不可用(有时也不支持.Position
)。不过,在MemoryStream
和FileStream
这样的情况下,你会逃脱惩罚。最好只使用while
而不是Try...(...)
方法,后者在找到流的末尾时返回false
。