34

I am sorry if this is a duplicate. I have searched several places for an answer that I might understand including:

ProtoBuf.net Base class properties is not included when serializing derived class

Serialize inherited classes using protobuf-net

My apologies but I did not really understand the answers. I am looking for a faster more compact binary serializer and ProtoBuf looks like it might be the answer. I need to serialize a set of classes that all derive from a single base class. There are a large number of them so before committing to edit the class code I ran a simple test. Also I do not want to modify the classes in any way that might impact deserializing older persisted files generated with the NET binary serializer.

This is the base class:

[ProtoContract]
    public class BaseClass
    {
        [ProtoMember(1)]
        public string Name
        {
            get; set;
        }
        [ProtoMember(2)]
        public int Age
        {
            get; set;
        }
    }

This is the derived class:

[ProtoContract]
    public class SubClass1 : BaseClass
    {
        [ProtoMember(3)]
        public string Town
        {
            get; set;
        }

        [ProtoMember(4)]
        public Sex Sex
        {
            get; set;
        }
    }

This is the code to serialize and deserialize (taken directly from the Getting Started Guide

var person = new SubClass1 { Age = 25, Name = "Fred", Town = "Denbigh", Sex = Sex.Female };

            using (var file = File.Create(filename))
            {
                Serializer.Serialize(file, person);
            }

and de -serialize:

SubClass1 newPerson;
            using (var file = File.OpenRead(filename))
            {
                newPerson = Serializer.Deserialize<SubClass1>(file);
            }

            MessageBox.Show(newPerson.Name + 
                " : " + newPerson.Town + 
                " : " + newPerson.Age.ToString() + 
                " : " + newPerson.Sex);

The result is " : Denbigh : 0 : Female"

Somehow the values from the base class properties are not being serialized? I originally tested it with the ProtoMember indices for the derived class as 1, 2. I kind of thought that would not work so went for 3, 4. It seems to make no difference. In my paranoia I ran the same test using the standard NET binary serializer and got the expected result: "Fred : Denbigh : 25 : Female"

What am I missing please?

Community
  • 1
  • 1
ScruffyDuck
  • 2,606
  • 3
  • 34
  • 50

2 Answers2

36

You need to use the ProtoInclude attribute on your base class:

[ProtoContract]
[ProtoInclude(500, typeof(SubClass1 ))]
public class BaseClass
{

The id arg (500 in the above example) should be unique to that class. See this article for more information

wal
  • 17,409
  • 8
  • 74
  • 109
  • @RenniePet its my site, down for maintenance, bad timing! – wal Aug 31 '13 at 12:19
  • @wal 500 id arg should be unique to subclass, or to all ProtoInclude attributes in solution? I believe its first, but you make it sound like its second – Valentin Kuzub Dec 12 '14 at 12:34
  • @ValentinKuzub definitely the former. I just like to use 500 to future proof the class in class it gets a lot more properties! `you make it sound like its second` <-- except where i said `(500 in the above example) should be unique to that class` ?? – wal Dec 12 '14 at 22:05
  • 11
    The design of this seems backwards. What if I have a base class that is in another library that doesn't (and shouldn't) know about the classes in my application? Can you add the tag number to the sub class instead? – Rush Frisby Dec 19 '14 at 20:15
  • @rushonerok I am fairly certain the answer is No. Your question is deserving of its own stackoverflow q (where Marc Gravell, protobuf owner will probably answer). If you dont define it in the base class then when deserializing the deserializer does not know what types to expect. it is not aware that (roughly speaking) something at the 500-th position could be a `typeof(SubClass1)` - at its core this is what keeps protobuf so small - ie no type information needs to be embedded in the serializaer (unlike `BinaryFormatter`) as with proto it is defined 'up front' cc @Marc Gravell – wal Dec 20 '14 at 13:58
  • 1
    @rushonerok I should also note: With v2 of protobuf-net you can build a serializer on the fly which means you don't have to decorate the classes with attributes see my article http://wallaceturner.com/serialization-with-protobuf-net I use this method in apps to handle the case you describe - that is I define a serialization model that knows (references) all the classes in my application that require serialization - this means your base classes can remain ignorant of any sub-classes and still serialize! – wal Dec 20 '14 at 14:00
  • I guess we'll never know @RushFrisby ... – Denny Aug 18 '22 at 06:29
1

I know it's quite old, but may help somebody. In some cases you can fix this by redefining base properties in your subtype:

[ProtoContract]
public class SubClass1 : BaseClass
{
    [ProtoMember(1)]
    public string BaseName
    {
        get{return base.Name;}
        set{base.Name = value;}
    }

    ...

    [ProtoMember(3)]
    public string Town
    {
        get; set;
    }

    [ProtoMember(4)]
    public Sex Sex
    {
        get; set;
    }
}
  • I don't think this will work because `SubClass1` will have two properties defined as `ProtoMember` with index=1 ( `Name` and `BaseName` ) – BaltoStar Nov 09 '16 at 00:43