我可以使用protobuf-net序列化任意类型吗?

时间:2011-08-12 21:28:45

标签: protobuf-net

我正在尝试使用protobuf-net序列化一些对象,但不幸的是,他们自由地使用了DateTimeOffset,而protobuf-net还没有支持它。这导致了很多:

  

没有为类型定义序列化程序:System.DateTimeOffset

我可以为未知类型定义自己的序列化例程吗? (之前曾问same question,但他的问题已得到解决。)

如果重要的话,我在.NET 4下使用最新的protobuf-net beta,v2.0.0.431。我也在使用运行时定义,因此我无法以声明方式指定如何处理某些属性。

3 个答案:

答案 0 :(得分:27)

有两种方法可以解决未知“常见”类型的问题;第一种是使用填充属性,例如将值表示为类似的属性(例如stringlong):

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

另一种方法是代理,这是第二个自动替换的protobuf合约。使用代理人的要求是:

  • 两种类型之间必须存在已定义的转换运算符(隐式或显式)(例如DateTimeOffsetDateTimeOffsetSurrogate
  • 然后使用SetSurrogate(surrogateType)来教育protobuf-net,例如RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));

垫片属性更简单,但需要重复每个成员。代理自动应用于模型中所有类型的实例。然后代理遵循标准的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));

答案 1 :(得分:16)

完全尊重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));

答案 2 :(得分:3)

以防任何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方面,这是不明显的。

您还可以使用Max的技巧来使用刻度来节省空间。

编辑:以下是如何将代理项添加到运行时类型模型中:

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