1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors-MessagePack-CSharp

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
migration.md 23 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 02.12.2024 11:31 8acacc8

Переход с MessagePack версии 1.x на версию 2.x

MessagePack 2.0 содержит множество критических изменений по сравнению с версиями 1.x. Они включают в себя как двоичные, так и исходные критические изменения, что означает, что вам может потребоваться обновить исходный код, а также перекомпилировать его с версией 2.x.

Версия 1.x всё ещё будет поддерживаться для исправлений безопасности, но новые функции, как правило, будут предлагаться только в версиях 2.x.

Обновите ссылки на пакеты с версии 1.x, которую вы используете, до версии 2.x. Если ваш проект компилируется, то вы можете закончить работу. В противном случае просмотрите каждую ошибку компилятора. Некоторые распространённые ошибки перечислены ниже с предложенными исправлениями.

Если у вас есть приложение, которое имеет смесь потребителей MessagePack, и не все из них могут быть обновлены до версии v2.x сразу, вы можете предложить сборки MessagePack v1.x и v2.x вместе со своим приложением, чтобы каждый пользователь мог найти то, что ему нужно. Вот пример.

Изменения API

MessagePackSerializerOptions

Новый класс MessagePackSerializerOptions становится основным в этой библиотеке. Он инкапсулирует IFormatterResolver, который раньше передавался сам по себе. Также он включает несколько других настроек, которые могут влиять на работу MessagePackSerializerOptions или некоторых форматеров.

Поскольку этот новый класс обычно сохраняется в общедоступных статических свойствах, он является неизменяемым, чтобы обеспечить безопасное совместное использование. Каждое свойство Foo в классе включает метод WithFoo, который клонирует экземпляр и возвращает новый экземпляр с изменённым только одним свойством.

Чтобы поддержать этот новый класс опций и избежать ненужных выделений от методов копирования и изменения, многие популярные резолверы теперь предоставляют общедоступное статическое свойство Options с предустановленным для себя резолвером. Например, вы можете использовать:

var msgpack = MessagePackSerializer.Serialize(objectGraph, StandardResolverAllowPrivate.Options);
var deserializedGraph = MessagePackSerializer.Deserialize<MyType>(msgpack, StandardResolverAllowPrivate.Options);

Если вы хотите объединить определённый резолвер с другими изменениями опций (например, включить сжатие LZ4), вы также можете это сделать:

var options = StandardResolverAllowPrivate.Options.WithCompression(MessagePackCompression.Lz4BlockArray);
var msgpack = MessagePackSerializer.Serialize(objectGraph, options);
var deserializedGraph = MessagePackSerializer.Deserialize<MyType>(msgpack, options);

Эквивалентный экземпляр опций можно создать вручную:

var options = MessagePackSerializerOptions.Standard
    .WithCompression(MessagePackCompression.Lz4BlockArray)
    .WithResolver(StandardResolverAllowPrivate.Instance);

Класс MessagePackSerializer

Сериализация

Сериализация графов объектов в msgpack теперь основана на IBufferWriter<byte> вместо ref byte[]. Это позволяет сериализовать очень большие графы объектов без многократного выделения всё больших массивов и копирования ранее сериализованных байтов msgpack из меньшего буфера в больший. IBufferWriter<byte> может направлять записанные байты msgpack непосредственно в канал, файл или куда угодно, позволяя вам также избегать копирования буфера в собственном коде.

IBufferWriter<byte> всегда оборачивается новой структурой MessagePackWriter.

Существует множество перегрузок метода Serialize, которые в конечном итоге вызывают перегрузку, принимающую MessagePackWriter.

Десериализация

Десериализация последовательностей msgpack теперь гораздо более гибкая. Вместо десериализации только из byte[] или ArraySegment<byte>, вы можете десериализоваться из любого экземпляра ReadOnlyMemory<byte> или ReadOnlySequence<byte>.

ReadOnlyMemory<byte> похож на ArraySegment<byte>, но более дружественен и может ссылаться на непрерывную память в любом месте, включая собственные указатели. Вы можете передать byte[] или ArraySegment<byte> везде, где ожидается ReadOnlyMemory<byte>, и C# неявно приведёт их для вас (без какого-либо копирования буфера).

ReadOnlySequence<byte> позволяет выполнять десериализацию из несмежно выделенной памяти, позволяя вам Deserialize очень большие последовательности Msgpack без риска OutOfMemoryException просто из-за невозможности найти большое количество свободной непрерывной памяти

Существует множество перегрузок метода Deserialize, которые в конечном итоге вызывают перегрузку, принимающую MessagePackReader.

Десериализация из потока

Десериализация из Stream изменилась с версии 1.x до версии 2.0. Параметр readStrict был удалён, и в версии 2.x методы MessagePackSerializer.Deserialize{Async}(Stream) действуют так же, как если бы readStrict: false в версии 1.x. Это отлично работает и является предпочтительным API для использования, когда ожидается, что весь Stream будет содержать ровно одну структуру верхнего уровня messagepack, которую вы хотите десериализовать.

Из соображений производительности весь поток считывается в память перед началом десериализации. Если в потоке больше данных, чем структура messagepack для десериализации, десериализация проигнорирует лишние данные, но лишних данных больше не будет в потоке, чтобы их можно было прочитать позже.

Если поток доступен для поиска (то есть его свойство CanSeek возвращает true), то после завершения десериализации поток будет перемещён на первый байт после структуры данных messagepack, которая была десериализована. Это означает, что вы получите поток обратно, как и ожидали, но только после того, как вы заплатили за производительность «чтения» большего количества данных, чем было необходимо для десериализации.

Если поток не доступен для поиска (например, сетевой поток) или содержит несколько структур данных верхнего уровня messagepack подряд, MessagePack 2.0 добавляет новый, более эффективный способ чтения каждой структуры messagepack. Он аналогичен режиму readStrict: true версии 1.x, но гораздо более эффективен. Он представлен в виде нового класса MessagePackStreamReader и может быть легко использован следующим образом:

static async Task<List<T>> DeserializeListFromStreamAsync<T>(Stream stream, CancellationToken cancellationToken)
{
    var dataStructures = new List<T>();
    using (var streamReader = new MessagePackStreamReader(stream))
    {
        while (await streamReader.ReadAsync(cancellationToken) is ReadOnlySequence<byte> msgpack)
        {
            dataStructures.Add(MessagePackSerializer.Deserialize<T>(msgpack, cancellationToken: cancellationToken));
        }
    }

    return dataStructures;
}

Поведение по умолчанию

Статическое свойство DefaultResolver было заменено статическим свойством DefaultOptions. Как и в случае с версией 1.x, в версии 2.x это статическое свойство влияет на то, как происходит сериализация, когда значение явно не указано при вызове одного из методов MessagePackSerializer.

ПРЕДУПРЕЖДЕНИЕ: При разработке простого приложения, где вы контролируете весь код, связанный с MessagePack, может быть безопасно полагаться на этот изменяемый статический элемент управления поведением. Для всех других библиотек или многоцелевых приложений, использующих MessagePackSerializer, вы должны явно указать MessagePackSerializerOptions для использования с каждым вызовом метода, чтобы гарантировать, что ваш код ведёт себя так, как вы ожидаете, даже при совместном использовании AppDomain или процесса с другими пользователями MessagePack, которые могут изменить это статическое свойство.

Неуниверсальные методы

В версии 1.x неуниверсальные методы сериализации/десериализации были представлены во вложенном классе MessagePackSerializer.NonGeneric. В версии 2.x эти перегрузки перемещены в сам класс MessagePackSerializer.

Вложенный класс MessagePackSerializer.Typeless в версии 1.x остаётся в версии 2.x, но с изменённым набором перегрузок.

Методы преобразования JSON

В версии 1.x класс MessagePackSerializer предоставлял методы как для сериализации графа объектов в JSON, так и для преобразования между msgpack и JSON. Эти два перевода сильно отличались друг от друга, но были всего лишь перегрузками друг друга. В версии 2.x эти методы были переименованы для ясности. Методы ConvertFromJson и ConvertToJson переводят между JSON и двоичным файлом msgpack. Метод SerializeToJson переводит граф объектов в JSON.

LZ4MessagePackSerializer

Класс LZ4MessagePackSerializer был удалён. Вместо этого используйте MessagePackSerializer и передайте LoadTypeCustomizedOptions(MessagePackSerializerOptions copyFrom)

: base(copyFrom) { }

internal LoadTypeCustomizedOptions(IFormatterResolver resolver)

: base(resolver) { }

public override Type LoadType(string typeName)

Type type = base.LoadType(typeName);
if (type == null)
{
    // custom logic here
}

return type;

}


Затем можно создать экземпляр этого типа параметров и передать его десериализатору:

```cs
var options = new LoadTypeCustomizedOptions(MessagePackSerializerOptions.Standard);
T value = MessagePackSerializer.Deserialize<T>(sequence, options);

Пользовательские форматы

Если вы написали пользовательскую реализацию IMessagePackFormatter<T>, вам придётся адаптироваться к изменениям интерфейса и API, используемым для реализации такого класса.

Интерфейс был изменён следующим образом:

 public interface IMessagePackFormatter<T> : IMessagePackFormatter
 {
-    int Serialize(ref byte[] bytes, int offset, T value, IFormatterResolver  formatterResolver);
+    void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options);
-    T Deserialize(byte[] bytes, int offset, IFormatterResolver  formatterResolver, out int readSize);
+    T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options);
 }

Обратите внимание на более простую сигнатуру метода для каждого метода. Вам больше не нужно иметь дело с необработанными массивами и смещениями. Статический класс MessagePackBinary из версии 1.x, который форматировщик использовал для записи кодов msgpack, заменён на MessagePackWriter и MessagePackReader. Эти две структуры включают API для записи и чтения msgpack и управляют базовыми буферами, поэтому вам больше не нужно это делать.

Рассмотрим следующий форматировщик версии 1.x для типа Int16:

class NullableInt16Formatter : IMessagePackFormatter<Int16?>
{
    public int Serialize(ref byte[] bytes, int offset, Int16? value, IFormatterResolver formatterResolver)
    {
        if (value == null)
        {
            return MessagePackBinary.WriteNil(ref bytes, offset);
        }
        else
        {
            return MessagePackBinary.WriteInt16(ref bytes, offset, value.Value);
        }
    }

    public Int16? Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize)
    {
        if (MessagePackBinary.IsNil(bytes, offset))
        {
            readSize = 1;
            return null;
        }
        else
        {
            return MessagePackBinary.ReadInt16(bytes, offset, out readSize);
        }
    }
}

После миграции на версию 2.x он выглядит так:

class NullableInt16Formatter : IMessagePackFormatter<Int16?>
{
    public void Serialize(ref MessagePackWriter writer, Int16? value, MessagePackSerializerOptions options)
    {
        if (value == null)
        {
            writer.WriteNil();
        }
        else
        {
            writer.Write(value.Value);
        }
    }

    public Int16? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
    {
        if (reader.TryReadNil())
        {
            return default;
        }
        else
        {
            return reader.ReadInt16();
        }
    }
}

Обратите внимание, что структура очень похожа, но массивы и смещения больше не нужны. Базовый формат msgpack не изменился, позволяя обновить код до версии 2.x при сохранении совместимости с файлом или сетевой стороной, которая использует MessagePack версии 1.x.

Тонкое изменение в именовании методов

При написании целых чисел шаблон имён методов изменился таким образом, что хотя ваш код v1.x->v2.0 будет компилироваться, он может создавать немного другой (и менее эффективный) двоичный файл msgpack по сравнению с предыдущим. Вот таблица перевода:

v1.x v2.x
MessagePackBinary.WriteMapHeaderForceMap32Block (удалено)
MessagePackBinary.WriteArrayHeaderForceArray32Block (удалено)
MessagePackBinary.WriteByteForceByteBlock MessagePackWriter.WriteUInt8(byte)
MessagePackBinary.WriteSByteForceSByteBlock MessagePackWriter.WriteInt8(sbyte)
------------------------------------------ --------------------------------------
MessagePackBinary.WriteInt64ForceInt64Block MessagePackWriter.WriteInt64(long)
MessagePackBinary.MessagePackBinary.WriteInt32ForceInt32Block MessagePackWriter.WriteInt32(int)
MessagePackBinary.WriteUInt16ForceUInt16Block MessagePackWriter.WriteUInt16(ushort)
MessagePackBinary.WriteUInt32ForceUInt32Block MessagePackWriter.WriteUInt32(uint)
MessagePackBinary.WriteUInt64ForceUInt64Block MessagePackWriter.WriteUInt64(ulong)
MessagePackBinary.WriteStringForceStr32Block (удалено)
MessagePackBinary.WriteExtensionFormatHeaderForceExt32Block (удалено)
MessagePackBinary.WriteMapHeader MessagePackWriter.WriteMapHeader
MessagePackBinary.WriteArrayHeader MessagePackWriter.WriteArrayHeader
MessagePackBinary.WriteByte MessagePackWriter.Write(byte)
MessagePackBinary.WriteBytes MessagePackWriter.Write(byte[])
MessagePackBinary.WriteSByte MessagePackWriter.Write(sbyte)
MessagePackBinary.WriteSingle MessagePackWriter.Write(float)
MessagePackBinary.WriteDouble MessagePackWriter.Write(double)
MessagePackBinary.WriteInt16 MessagePackWriter.Write(short)
MessagePackBinary.WriteInt32 MessagePackWriter.Write(int)
MessagePackBinary.WriteInt64 MessagePackWriter.Write(long)
MessagePackBinary.WriteUInt16 MessagePackWriter.Write(ushort)
MessagePackBinary.WriteUInt32 MessagePackWriter.Write(uint)
MessagePackBinary.WriteUInt64 MessagePackWriter.Write(ulong)
MessagePackBinary.WriteChar MessagePackWriter.Write(char)
MessagePackBinary.WriteStringBytes MessagePackWriter.WriteString(ReadOnlySpan)
MessagePackBinary.WriteString MessagePackWriter.Write(string)
MessagePackBinary.WriteExtensionFormatHeader MessagePackWriter.WriteExtensionFormatHeader
MessagePackBinary.WriteExtensionFormat MessagePackWriter.WriteExtensionFormat
MessagePackBinary.WriteDateTime MessagePackWriter.Write(DateTime) (notes)

Суть в том, что обычно вы можете просто вызывать MessagePackWriter.Write(*) для примитивных типов, и будет записан наиболее эффективный двоичный файл msgpack. Вы должны вызывать явные методы WriteX(x), только если вам нужно принудительно записать определённый (фиксированный) формат значения.

Что касается методов чтения целых чисел, они гораздо более взаимозаменяемы, чем в версии v1.x. Вы можете вызвать любой метод ReadInt* или ReadUInt*, и он успешно прочитает целочисленное значение и подгонит его под желаемый тип возвращаемого значения, если значение не переполняется. Например, вы можете вызвать Write(byte), а позже прочитать значение с помощью ReadInt32(). Можно даже вызвать Write(long), а затем прочитать его с помощью ReadByte(), и это сработает, пока фактическое значение помещается в byte. Если целочисленное значение превышает максимальное или минимальное значение, которое может быть сохранено требуемым типом возвращаемого значения, генерируется исключение OverflowException.

Поведенческие изменения

DateTime

При записи DateTime версия v1.x всегда вызывала DateTime.ToUniversalTime() перед сериализацией значения. В версии v2.x мы вызываем этот метод, только если DateTime.Kind == DateTimeKind.Local. Это означает, что если вы записывали DateTimeKind.Unspecified, то сериализованное значение больше не будет изменяться при необоснованном предположении, что базовое значение было Local. Вы должны явно указывать DateTimeKind для всех значений DateTime. При обновлении до MessagePack v2.x это критическое изменение, если ваши значения Unspecified фактически представляли местное время и нуждались в преобразовании.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/mirrors-MessagePack-CSharp.git
git@api.gitlife.ru:oschina-mirror/mirrors-MessagePack-CSharp.git
oschina-mirror
mirrors-MessagePack-CSharp
mirrors-MessagePack-CSharp
master