Недопустимое поле в исходных данных: 0 исключение при использовании

Я сериализую многие объекты и записываю их в поток, а затем десериализую эти объекты из потока с помощью 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);
            }
        }

person wei zheng    schedule 25.12.2015    source источник


Ответы (1)


Это немного разные сценарии из-за необязательного аргумента. Используемые вами методы SerializeWithLengthPrefix и DeserializeWithLengthPrefix имеют необязательный аргумент, который, если он опущен, по умолчанию равен нулю. Это означает, что вы сериализуете с нулем, но без номера поля (нулевой номер поля никогда не допустим в protobuf). Некоторым это нужно. Существует еще одна перегруженная версия, позволяющая указать номер поля , а также префикс длины.

Перегрузка TryDeserializeWithLengthPrefix, которую вы используете, предназначена для использования во втором сценарии — в частности, версия, которую вы используете с делегатом, предназначена для того, чтобы вы могли сообщить сериализатору, какой тип он должен думать о на основе поля -number (в вашем случае 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, чтобы указать эту точную длину.

В качестве примечания: обычно вам следует избегать проверки .Length против .Position в Stream, так как .Length часто недоступен (и .Position иногда тоже не поддерживается). Однако вам это сойдет с рук в таких вещах, как MemoryStream и FileStream. Лучше просто использовать while вместо метода Try...(...), который возвращает false, если находит конец потока.

person Marc Gravell    schedule 25.12.2015
comment
Большое спасибо, это работает, вы спасли мой день. Не могли бы вы привести простой пример использования ProtoReader? Это может сэкономить мне байт каждый раз, когда я сериализую объект. - person wei zheng; 28.12.2015
comment
@wei IIRC есть перегрузки (особенно для неуниверсального API) десериализации, которые принимают считыватель; вы просто new читатель для нужной длины (используйте RuntimeTypeModel.Default в качестве модели и null в качестве контекста) и передаете его... - person Marc Gravell; 28.12.2015