1

I get that protobuf do not support inheritance, and since I am using protoc-c on another end, I do not want to use any extensions as well. However, I am stuck with a C# model that depends on inheritance:

class Header {
     public int version { get; set; }
}

class Message : Header {
     public String message { get; set; }
}

I attempted to switch the inheritance into encapsulation in the wire format to something like this:

[ProtoContract]
class Header {
     [ProtoMember(1)]
     public int version { get; set; }
}

[ProtoContract]
class Message : Header {
     [ProtoMember(1)]
     public Header Header { get { return this; } set { } }

     [ProtoMember(2)]
     public String Message { get; set; }
}

Then I get the "Unexpected sub-type" error which prompts me to: Why I have to use [ProtoInclude]?

I feel that my case is different than the case in the above question, so would like to ask again for my specific case, where I have tried to inside out the inheritance, is this impossible to do without having ProtoInclude?

If not, how would I do it in v2?

----- EDITED ------

My proto file in the C (using protobuf-c) side is something like this:

message Header {
    optional int32 version = 1;
}

message Message {
    optional Header header = 1;
    optional string message = 2;
}

I do not want to put the Message inside the Header, and I do not want the inheritance-over-the-wire feature. This format enables me to add stuffs into the Header message easily without needing to change the Message message.

Community
  • 1
  • 1
Phuah Yee Keat
  • 1,572
  • 1
  • 17
  • 17
  • What is `A` in the first example, and how does it relate to the second example? In the first example, what is the relationship between `Header` and `Message` ? Additionally, in the second example, how does `Message` return `this` for a `Header`, when `Message` is not a `Header` ? (aka, that code doesn't compile, so I'm unclear on whether it represents reality) – Marc Gravell Aug 14 '12 at 07:27
  • Sorry, originally I used A and B, I have switched to use Message:Header but did not rename them all. I have updated it. Basically the first and second example is the same, with the second just decorated with Protobuf stuffs. – Phuah Yee Keat Aug 15 '12 at 03:14
  • Added the proto message that I am using (and do not wish to change) in the C-side for better clarity of the question. – Phuah Yee Keat Aug 15 '12 at 03:20

1 Answers1

0

With the edit: no, that scenario is not directly supported - protobuf-net is highly aware of inheritance, and isn't very amenable to ignoring it. This seems such an unusual case that I'm not desperate to add it, and I thing the return this; getter and no-op setter would cause additional downstream complications (not bugs, since it isn't expected to support that) which could be pretty hard to rectify.

I would advise using a model that is similar to the structure you want to represent. If this isn't directly possible, you can use a surrogate instead. The following works and retains both your intended wire-structure and the existing type inheritance:

using ProtoBuf;
using ProtoBuf.Meta;

// DTO model - maps directly to the wire layout
[ProtoContract]
class HeaderDto
{
    [ProtoMember(1)]
    public int Version { get; set; }
}

[ProtoContract]
class MessageDto
{
    [ProtoMember(1)]
    public HeaderDto Header { get { return header;}}
    private readonly HeaderDto header = new HeaderDto();

    [ProtoMember(2)]
    public string Message { get; set; }

    // the operators (implicit or explicit) are used to map between the
    // primary type (Message) and the surrogate type (MessageDto)
    public static implicit operator Message(MessageDto value)
    {
        return value == null ? null : new Message {
            version = value.Header.Version, message = value.Message };
    }
    public static implicit operator MessageDto(Message value)
    {
        return value == null ? null : new MessageDto {
            Message = value.message, Header = { Version = value.version } };
    }
}

// domain model
class Header
{
    public int version { get; set; }
}

class Message : Header
{
    public string message { get; set; }
}

// example
static class Program
{
    static void Main()
    {
        // configure the surrogate
        RuntimeTypeModel.Default.Add(typeof(Message), false)
                        .SetSurrogate(typeof(MessageDto));
        Message msg = new Message { version = 1, message = "abc" };
        var obj = Serializer.DeepClone(msg);
    }
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks for the explanation, I have updated the question above, basically I do not want the inheritance-over-the-wire format. After twiddling through the code, I was sort of looking for something like [ProtoMember(1, LookForMoreSpecializedType=false)], so that when the Header property returns a Header class, it will just serialize the Header class without trying to figure out that its a Message class and trying to serialize it as a Message class instead. – Phuah Yee Keat Aug 15 '12 at 03:22