Подтвердить что ты не робот

Могу ли я сериализовать произвольные типы с protobuf-net?

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

Серийный определитель не задан для типа: System.DateTimeOffset

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

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

4b9b3361

Ответ 1

Существует два способа приближения к проблеме неизвестных "общих" типов; во-первых, использовать свойство 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, поэтому вы указываете, какие члены сериализуются и т.д.

EDIT: добавление примера кода

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));

Ответ 2

При всем уважении к Marc Gravell ответ, если вы заботитесь о размере сериализованных данных, вы должны использовать следующий суррогатный класс. Размер вывода составляет 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));