6

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");
}
Qwertie
  • 16,354
  • 20
  • 105
  • 148

1 Answers1

6

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!

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    What's a DTO? I guess some kind of ordinary class. So protobuf-net will use reflection to find the implicit/explicit conversion? – Qwertie Aug 23 '13 at 18:27
  • @Qwertie yeah, by DTO I just mean a regular class intended for use in the serializer. The operators are probed via reflection, but the library makes extensive use of metal programming: there is very little reflection during usage - only during discovery – Marc Gravell Aug 23 '13 at 18:30
  • 2
    BTW, is it possible to serialize a particular class as a primitive type (e.g. suppose I want to save just the font's size) or a binary blob (e.g. `System.Drawing.PointF` => 8 byte blob)? – Qwertie Aug 23 '13 at 19:50