Qwertie
Qwertie

Reputation: 17216

How to serialize a closed immutable type with protobuf-net?

For example, I would like to serialize and deserialize System.Drawing.Font which is immutable and cannot be altered to suit protobuf-net conventions. In general, is it possible to write some sort of "custom" serializer in protobuf-net?

Edit: Based on the accepted answer, here are examples of proxies for System.Drawing:

[ProtoContract]
struct ProtoColor
{
    [ProtoMember(1, DataFormat=DataFormat.FixedSize)]
    public uint argb;
    public static implicit operator Color(ProtoColor c) 
        { return Color.FromArgb((int)c.argb); }
    public static implicit operator ProtoColor(Color c)
        { return new ProtoColor { argb = (uint)c.ToArgb() }; }
}
[ProtoContract()]
class ProtoFont
{
    [ProtoMember(1)]
    string FontFamily;
    [ProtoMember(2)]
    float SizeInPoints;
    [ProtoMember(3)]
    FontStyle Style;

    public static implicit operator Font(ProtoFont f) {
        return new Font(f.FontFamily, f.SizeInPoints, f.Style);
    }
    public static implicit operator ProtoFont(Font f) { 
        return f == null ? null : new ProtoFont { 
            FontFamily = f.FontFamily.Name, 
            SizeInPoints = f.SizeInPoints, 
            Style = f.Style };
    }
}
[ProtoContract()]
class ProtoStringFormat
{
    [ProtoMember(1, DataFormat=DataFormat.Group)]
    StringAlignment Alignment;
    [ProtoMember(2)]
    StringAlignment LineAlignment;
    [ProtoMember(3)]
    StringFormatFlags Flags;
    public static implicit operator StringFormat(ProtoStringFormat f) { 
        return new StringFormat(f.Flags) { Alignment = f.Alignment, 
            LineAlignment = f.LineAlignment };
    }
    public static implicit operator ProtoStringFormat(StringFormat f) { 
        return f == null ? null : new ProtoStringFormat() { 
            Flags = f.FormatFlags, Alignment = f.Alignment, 
            LineAlignment = f.LineAlignment };
    }
}

// Before serializing or deserializing...
static RuntimeTypeModel Model;
static StaticConstructor()
{
    Model = TypeModel.Create();
    Model.AllowParseableTypes=true;
    Model.Add(typeof(Color), false).SetSurrogate(typeof(ProtoColor));
    Model.Add(typeof(Font), false).SetSurrogate(typeof(ProtoFont));
    Model.Add(typeof(StringFormat), false)
         .SetSurrogate(typeof(ProtoStringFormat));
    Model.Add(typeof(PointF), true).Add("X", "Y");
}

Upvotes: 6

Views: 1735

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1064114

Yes. Note that many immutable types will be handled by the "auto tuple" code - if it has a constructor that accepts parameters that look just like the public members, it can infer the behaviour from that.

Other than that, you can write your own DTO that has whatever layout / members you need, and add conversion operators (implicit or explicit) to and from your custom DTO and the target type (Font in this case). Your operator would have the code to get from one to the other. Then call:

 RuntimeTypeModel.Default.Add(typeof(Font), false)
    .SetSurrogate(typeof(YourCustomDTO));

The serializer will use the operators to change between the two types, and use the custom DTO on the wire.

Watch out for the incoming value to the operator being null!

Upvotes: 6

Related Questions