views:

90

answers:

2

I am prototyping Protobuf-net to replace some of our existing C# code which is currently using [Datacontract] to Serialize objects to Xml.

Using protobuffer we can easily share data with Java. I am thus very interested in the .proto file generation of Protobuf-net. This worked well for almost all the use cases I tested so far.

But now with inheritance it's a different ball game. The .proto file that's been generated for the Inherited classes is very simple - not including any of the fields for the Base Class.

The inheritance itself is working fine in C# - I can read the generated byte stream (see my test below). So the internal binary stream contains all the fields of the base class

The generated .proto:

message ProtoScholar {
   optional string Subject = 1;
}

If I can understand how the byte stream is written out I can create the corresponding .proto file by hand.

Anyone got any experience in creating a .proto file for inheritance using protobuf-net?

Any info on how the data stream is being created for inheritance will be great.

My DataModel is as follows:

[DataContract]
[ProtoInclude(7, typeof(ProtoScholar))]
public class ProtoAlien
{

    [DataMember(Order = 1)]
    public string Name
    {
        get;
        set;
    }


    [DataMember(Order = 2)]
    public double? Average
    {
        get;
        set;
    }

    [DataMember(Order = 3)]
    public int? HowMuch
    {
        get;
        set;
    }

    [DataMember(Order = 4, IsRequired = true)]
    public Height Length
    {
        get; set;
    }


    [DataMember(Order = 5, IsRequired = true)]       
    public Character Personality
    {
        get;
        set;
    }

    [DataMember(Order = 6, IsRequired = true)]       
    public DateTime When
    {
        get; set;
    }

    public enum Height
    {
        Short = 1,
        Medium,
        Tall
    }

    public enum Character : long
    {
        Wasp = 1717986918,
        BumbleBee,
        WorkerBee,
        Hornet,
        Queen
    }        
}

[DataContract()]
public class ProtoScholar : ProtoAlien
{

    [DataMember(Order=1)]
    public string Subject
    {
        get; set;
    }

}

My NUnit test looks like this:

[Test]
    public void TestInheritanceSupport()
    {

        var protoBuffer = new ProtoScholar
        {
            Name = "Ben11",
            HowMuch = null,
            Length = ProtoAlien.Height.Tall,
            Personality = ProtoAlien.Character.WorkerBee,
            When = new DateTime(2010, 4, 1, 2, 33, 56, 392),
            Subject = "Alien Economics"
        };


        using (var ms = new MemoryStream())
        {

            var proto = Serializer.GetProto<ProtoScholar>();
            Console.WriteLine(proto);

            //Serialize to a Memory Stream
            Serializer.Serialize(ms, protoBuffer);

            Console.WriteLine(ms.Length);
            ms.Position = 0;
            var deserializedProtoBuffer = Serializer.Deserialize<ProtoScholar>(ms);

            Assert.AreEqual("Ben11", deserializedProtoBuffer.Name);

            Assert.Null(deserializedProtoBuffer.HowMuch);
            Assert.AreEqual(ProtoAlien.Height.Tall, deserializedProtoBuffer.Length);

            Assert.AreEqual(ProtoAlien.Character.WorkerBee, deserializedProtoBuffer.Personality);
            Assert.AreEqual(new DateTime(2010, 4, 1, 2, 33, 56, 392), deserializedProtoBuffer.When);
            Assert.AreEqual("Alien Economics", deserializedProtoBuffer.Subject);

        }

    }
+1  A: 

Since inheritance is not part of the core spec, I basically represent this using encapsulation. So your [ProtoInclude] maps to:

message ProtoAlien {
   // other fields 1 thru 6 [snip]
   optional ProtoScholar ProtoScholar = 7;
}
message ProtoScholar {
   optional string Subject = 1;
}

GetProto<T>() is undergoing an overhaul in v2, so it should support a few more of these scenarios.

Marc Gravell
Ok, I'll give that a go. The encapsulation approach makes sense in the absence of a core spec. But wouldn't it make more sense to Encapsulate the Base class into the Inherited Class?
André Vermeulen
Indeed! Generated the java code from the .proto files and it's working.
André Vermeulen
@André in the general case, that created some issues with representing things as their sub/super type correctly. i.e. if the prop is `SomeBaseType`, and you want to give it a `SomeSuperType` instance. Encapsulated from the super-type, we'd only know about the `SomeBaseType` in terms of the message structure. So inheritance wouldn't work on the wire.
Marc Gravell
A: 

Hi Marc,

Your current implementation basically generates a composite message inside base type.

If we do the other way around, generating the subclass message + super class fields, would this be more like a proper inheritance implementation?

And it would work for Java/wire-format side as well (do the same logic in .proto code generator piece). The only downside on the Java/wire-format side is that it doesn't know the inheritance hierarchy structure so only a full subclass message will be generated. But this is still better than the current composite approach which we must let super type knows all possible subclasses.

Can you correct me if I missed anything here? do you think it's achievable or not? if so, is it a good idea for me to branch v1 to try this?

Inheritance is a must have feature for our adoption.

Thanks in advance. Charles

Charles Cai
Sorry, I didn't see this, as new answers don't show anywhere special; you've e-mailed me separately - I'll reply to that.
Marc Gravell

related questions