Вы пытаетесь добавить объект (здесь List<struct_realTime>
), сериализованный с помощью MessagePackSerializer
в файл, содержащий уже сериализованную последовательность похожих объектов, точно так же, как это возможно с _3 _, protobuf-net или Json.NET. Позже вы, вероятно, захотите иметь возможность десериализовать всю последовательность в список или массив объектов того же типа.
В вашем коде три проблемы: две простые и одна фундаментальная.
Вот простые проблемы:
На самом деле вы не пишете fileStream
. Вместо этого сделайте следующее:
// Append each list_temp sequentially
using (var fileStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
MessagePackSerializer.Serialize(fileStream, list_temp);
}
Вы не отметили struct_realTime
с помощью [MessagePackObject]
атрибутов. Это может быть реализовано, например, следующее:
[MessagePackObject]
public struct struct_realTime
{
[Key(0)]
public int indexNum { get; set; }
[Key(1)]
public string currentTime { get; set; }
[Key(2)]
public string currentType { get; set; }
}
Сделав это, теперь вы можете многократно сериализовать list_temp
в файл ... но потом вы не сможете их прочитать! Это потому, что MessagePackSerializer
, кажется, читает весь файл при десериализации корневого объекта, пропуская любые дополнительные данные, добавленные в файл. Таким образом, следующий код не будет работать, потому что из файла будет прочитан только один объект:
List<List<struct_realTime>> allItemsInFile = new List<List<struct_realTime>>();
using (var fileStream = File.OpenRead(filename))
{
while (fileStream.Position < fileStream.Length)
{
allItemsInFile.Add(MessagePackSerializer.Deserialize<List<struct_realTime>>(fileStream));
}
}
Assert.IsTrue(allItemsInFile.Count == expectedNumberOfRootItemsInFile);
Демо-скрипт №1 здесь.
И код, подобный следующему, потерпит неудачу, потому что (первый) корневой объект в потоке не является массивом массивов объектов, а скорее всего одним массивом:
List<List<struct_realTime>> allItemsInFile;
using (var fileStream = File.OpenRead(filename))
{
allItemsInFile = MessagePackSerializer.Deserialize<List<List<struct_realTime>>>(fileStream);
}
Assert.IsTrue(allItemsInFile.Count == expectedNumberOfRootItemsInFile);
Демо-скрипт №2 здесь.
Поскольку MessagePackSerializer
, похоже, не имеет возможности десериализовать несколько корневых объектов из потока, каковы ваши варианты? Во-первых, вы можете десериализовать List<List<struct_realTime>>
, добавить к нему, а затем сериализовать все обратно в файл. Предположительно, вы не хотите этого делать по соображениям производительности.
Во-вторых, напрямую используя спецификацию MessagePack, вы можете вручную искать начало файла для анализа и перезаписи в соответствующем формате array 32
заголовок, затем перейдите к концу файла и используйте MessagePackSerializer
для сериализации и добавления нового элемента. Следующий метод расширения выполняет свою работу:
public static class MessagePackExtensions
{
const byte Array32 = 0xdd;
const int Array32HeaderLength = 5;
public static void AppendToFile<T>(Stream stream, T item)
{
if (stream == null)
throw new ArgumentNullException(nameof(stream));
if (!stream.CanSeek)
throw new ArgumentException("!stream.CanSeek");
stream.Position = 0;
var buffer = new byte[Array32HeaderLength];
var read = stream.Read(buffer, 0, Array32HeaderLength);
stream.Position = 0;
if (read == 0)
{
FormatArray32Header(buffer, 1);
stream.Write(buffer, 0, Array32HeaderLength);
}
else
{
var count = ParseArray32Header(buffer, read);
FormatArray32Header(buffer, count + 1);
stream.Write(buffer, 0, Array32HeaderLength);
}
stream.Position = stream.Length;
MessagePackSerializer.Serialize(stream, item);
}
static void FormatArray32Header(byte [] buffer, uint value)
{
buffer[0] = Array32;
buffer[1] = unchecked((byte)(value >> 24));
buffer[2] = unchecked((byte)(value >> 16));
buffer[3] = unchecked((byte)(value >> 8));
buffer[4] = unchecked((byte)value);
}
static uint ParseArray32Header(byte [] buffer, int readCount)
{
if (readCount < 5 || buffer[0] != Array32)
throw new ArgumentException("Stream was not positioned on an Array32 header.");
int i = 1;
//https://stackoverflow.com/questions/8241060/how-to-get-little-endian-data-from-big-endian-in-c-sharp-using-bitconverter-toin
//https://stackoverflow.com/a/8241127 by https://stackoverflow.com/users/23354/marc-gravell
var value = unchecked((uint)((buffer[i++] << 24) | (buffer[i++] << 16) | (buffer[i++] << 8) | buffer[i++]));
return value;
}
}
Его можно использовать для добавления вашего list_temp
следующим образом:
// Append each entry sequentially
using (var fileStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
MessagePackExtensions.AppendToFile(fileStream, list_temp);
}
А затем, чтобы десериализовать весь файл, выполните:
List<List<struct_realTime>> allItemsInFile;
using (var fileStream = File.OpenRead(filename))
{
allItemsInFile = MessagePackSerializer.Deserialize<List<List<struct_realTime>>>(fileStream);
}
Примечания:
Протокол MessagePack имеет 3 различных формата массивов:
fixarray
stores an array whose length is up to 15 elements.
array 16
хранит массив длиной до (2 ^ 16) -1 элементов.
array 32
хранит массив длиной до (2 ^ 32) -1 элементов.
Метод расширения требует, чтобы корневой массив был array 32
, чтобы исключить необходимость переформатировать весь массив, когда новый размер становится больше, чем емкость fixarray
или array 16
. MessagePackSerializer
, однако, всегда будет записывать в наиболее компактный формат, поэтому добавление к коллекции, ранее сериализованной с помощью MessagePackSerializer
, не гарантируется.
Если вы хотите использовать быстрый двоичный сериализатор, который не требует подсчета или размера массива в начале файла, тем самым поддерживая операции добавления из коробки, подумайте о protobuf-net. Подробнее см. У меня есть один файл, и мне нужно сериализовать несколько объектов случайным образом. Как я могу использовать C #? и Как добавить объект в файл при сериализации с помощью c # protobuf -net?.
Общий обзор использования этого сериализатора см. На странице https://github.com/protobuf-net/protobuf-net#protobuf-net и Protobuf-net: неофициальное руководство. Вам нужно будет пометить свои типы атрибутами, аналогичными атрибутам MessagePackSerializer
.
Демо-скрипт №3 здесь.
person
dbc
schedule
11.11.2019
BinaryFormatter
для этой (или любой другой) цели см. Каковы недостатки встроенной сериализации .Net на основе BinaryFormatter?. - person dbc   schedule 12.11.2019