";源数据中的无效字段:0”;使用时出现异常

本文关键字:异常 字段 数据 quot 无效 | 更新日期: 2023-09-27 18:28:05

我正在序列化许多对象并将其写入流,然后使用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);
            }
        }

";源数据中的无效字段:0”;使用时出现异常

由于有一个可选的参数,它们是略有不同的场景。您使用的SerializeWithLengthPrefixDeserializeWithLengthPrefix方法有一个可选参数,如果省略,则默认为零。这意味着您正在使用零进行序列化,但没有字段号(零字段号在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)。不过,在MemoryStreamFileStream这样的情况下,你会逃脱惩罚。最好只使用while而不是Try...(...)方法,后者在找到流的末尾时返回false