Могу ли я сериализовать произвольные типы с помощью protobuf-net? - PullRequest
21 голосов
/ 13 августа 2011

Я пытаюсь сериализовать некоторые объекты с protobuf-net , но, к сожалению, они широко используют DateTimeOffset, который еще не поддерживается protobuf-net.Это приводит к большому количеству:

Не определен сериализатор для типа: System.DateTimeOffset

Можно ли определить собственную процедуру сериализации для неизвестных типов?( тот же вопрос был задан ранее, но его проблема была решена.)

Я использую последнюю protobuf-net бета , v2.0.0.431, под .NET 4, если это имеет значение.Я также использую определения времени выполнения, поэтому у меня нет возможности декларативно указать, как должны обрабатываться определенные свойства.

Ответы [ 3 ]

27 голосов
/ 13 августа 2011

Существует два способа решения неизвестных «общих» типов; во-первых, использовать свойство shim, например свойство, которое представляет значение как нечто похожее (например, string или long):

[ProtoMember(8)]
public string Foo {
    get { ... read from the other member ... }
    set { ... assign the other member ... }
}

Другой подход - суррогат , который является вторым контрактом protobuf, который автоматически подставляется. Требования к использованию суррогата:

  • должен быть определенный оператор преобразования (неявный или явный) между двумя типами (например, DateTimeOffset и DateTimeOffsetSurrogate)
  • затем вы используете SetSurrogate(surrogateType) для обучения protobuf-net, например RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));

свойство shim проще, но требует повторения для каждого члена. Суррогат применяется автоматически ко всем экземплярам типа в модели. Затем суррогат следует стандартным правилам protobuf-net, поэтому вы можете указать, какие элементы сериализировать и т. Д.

РЕДАКТИРОВАТЬ: Пример добавления кода

using System;
using ProtoBuf;

[ProtoContract]
public class DateTimeOffsetSurrogate
{
    [ProtoMember(1)]
    public string DateTimeString { get; set; }

    public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
    {
        return new DateTimeOffsetSurrogate {DateTimeString = value.ToString("u")};
    }

    public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
    {
        return DateTimeOffset.Parse(value.DateTimeString);
    }
}

Тогда зарегистрируйте это так

RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
16 голосов
/ 20 сентября 2013

При всем уважении к ответу Марка Гравелла, если вам важен размер сериализованных данных, вы должны использовать следующий суррогатный класс. Выходной размер составляет 21 байт вместо 35 байт.

using System;
using ProtoBuf;

[ProtoContract]
public class DateTimeOffsetSurrogate
{
    [ProtoMember(1)]
    public long DateTimeTicks { get; set; }
    [ProtoMember(2)]
    public short OffsetMinutes { get; set; }

    public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
    {
        return new DateTimeOffsetSurrogate
        {
            DateTimeTicks = value.Ticks,
            OffsetMinutes = (short)value.Offset.TotalMinutes
        };
    }

    public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
    {
        return new DateTimeOffset(value.DateTimeTicks, TimeSpan.FromMinutes(value.OffsetMinutes));
    }
}

А потом регистрировать его абсолютно так же:

RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
3 голосов
/ 07 июня 2018

На всякий случай, если любой F # разработчик столкнется с этим вопросом, вот ответ на F #:

[<ProtoContract>]
type DateTimeOffsetSurrogate() =
    [<ProtoMember(1)>]
    member val DateTimeString = "" with get, set
    static member public op_Implicit(value : DateTimeOffset) : DateTimeOffsetSurrogate =
        DateTimeOffsetSurrogate(DateTimeString = value.ToString("o"))
    static member public op_Implicit(value : DateTimeOffsetSurrogate) : DateTimeOffset =
        DateTimeOffset.Parse(value.DateTimeString)

Это аспект op_Implicit, который неочевиден.

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

Редактировать: Вот как добавить суррогат в модель типа во время выполнения:

let init() =
    ProtoBuf.Meta.RuntimeTypeModel.Default.Add(typedefof<DateTimeOffset>, false).SetSurrogate(typedefof<DateTimeOffsetSurrogate>)
...