Как вы конфигурируете RuntimeModel.Default в protobuf-net для поддержки сериализации / десериализации SessionSecurityToken?

BinaryFormatter может выполнять сериализацию просто:

private byte[] TokenToBytes(SessionSecurityToken token)
{
    if (token == null)
    {
        return null;
    }

    using (var memoryStream = new MemoryStream())
    {
        var binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(memoryStream, token);
        return memoryStream.ToArray();
    }
}

Когда я попытался заменить BinaryFormatter на protobuf-Net:

using (var memoryStream = new MemoryStream())
{
    Serializer.Serialize(memoryStream, token);
    return memoryStream.ToArray();
}

Я получаю следующее исключение:

Тип не ожидается, и никакой контракт не может быть выведен: System.IdentityModel.Tokens.SessionSecurityToken

Я попытался добавить:

RuntimeTypeModel.Default.Add(typeof(SessionSecurityToken), true);

Который проходит через исключение, но теперь я получаю нулевой байтовый массив.

Как правильно настроить protobuf-Net для сериализации SessionSecurityToken?

С другой стороны, SessionSecurityToken не имеет конструктора без параметров.

using (var memoryStream = new MemoryStream(tokenAsBytes))
{
    return Serializer.Deserialize<SessionSecurityToken>(memoryStream);
}

Выдает исключение ProtoException:

Не найден конструктор без параметров для SessionSecurityToken

BinaryFormatter может сделать это без суеты

using (var memoryStream = new MemoryStream(bytes))
{
    var binaryFormatter = new BinaryFormatter();
    return (SessionSecurityToken)binaryFormatter.Deserialize(memoryStream);
}

Как правильно настроить protobuf-Net для десериализации SessionSecurityToken?

1 ответ

Решение

protobuf-net не претендует на возможность сериализации каждого отдельного типа; действительно, вам будет очень трудно сериализовать это через большинство сериализаторов (XmlSerializer любой из сериализаторов JSON, DataContractSerializer, так далее). BinaryFormatter находится в другой категории сериализаторов - и в этом конкретном случае реализует пользовательскую сериализацию через ISerializable.GetObjectData(SerializationInfo, StreamingContext),

Конструктор это красная сельдь; на самом деле protobuf-net может полностью обойти конструкторов, и в этом конкретном сценарии BinaryFormatter использует пользовательский конструктор сериализации через .ctor(SerializationInfo, StreamingContext),

В простых случаях protobuf-net можно настроить с помощью атрибутов или параметров времени выполнения; для более сложных сценариев можно использовать суррогаты для отображения между представлениями - однако в этом случае я бы предложил (глядя на реализацию SessionSecurityToken) что это сложнее, чем вы, вероятно, хотите сохранить.

Я бы отступил на шаг или два здесь; большинство сериализаторов предназначены для работы с данными, а не для реализации, и прекрасно работают с DTO и т. д. SessionSecurityToken это очень не DTO, и не существует простого способа переключения между ними. Мое сильное предложение здесь будет: сериализовать то, что это представляет, а не то, что это. Однако, если это является частью существующей сложной модели и ее действительно трудно выделить, вы можете вернуться к BinaryFormatter за эти биты. Я не проверял это, но подумайте:

RuntimeTypeModel.Default.Add(typeof(SessionSecurityToken), false)
        .SetSurrogate(typeof(BinaryFormatterSurrogate<SessionSecurityToken>));

С:

[ProtoContract]
public class BinaryFormatterSurrogate<T>
{
    [ProtoMember(1)]
    public byte[] Raw { get; set; }

    public static explicit operator T(BinaryFormatterSurrogate<T> value)
    {
        if(value==null || value.Raw == null) return default(T);
        using(var ms = new MemoryStream(value.Raw))
        {
            return (T)new BinaryFormatter().Deserialize(ms);
        }
    }
    public static explicit operator BinaryFormatterSurrogate<T>(T value)
    {
        object obj = value;
        if (obj == null) return null;
        using (var ms = new MemoryStream())
        {
            new BinaryFormatter().Serialize(ms, obj);
            return new BinaryFormatterSurrogate<T> { Raw = ms.ToArray() };
        }

    }
}

Имейте в виду, что это просто встраивает вывод одного сериализатора как необработанные данные в другой. К счастью, protobuf-net хорошо говорит о двоичном коде, так что это не добавит заметных накладных расходов (только префикс заголовка и длины для большого двоичного объекта), но также не будет делать ничего особенно умного или умного с SessionSecurityToken экземпляров. Если это единственное, что вы сериализуете, это действительно того не стоит. Если это всего лишь один уродливый удар в более крупной модели DTO, где большинство из них может хорошо сериализоваться - тогда это может сделать работу за вас.

Другие вопросы по тегам