How to Serialize Inherited Class with ProtoBuf-Net

17,077

Solution 1

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

Solution 2

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;
    }
}
Share:
17,077
ScruffyDuck
Author by

ScruffyDuck

[email protected] I am a Software Developer and Microsoft MVP. I work with NET and C#. I primarily develop tools for Flight Simulators such as MS Flight Sims and Lockheed Martin Prepar3D.

Updated on June 14, 2022

Comments

  • ScruffyDuck
    ScruffyDuck almost 2 years

    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?

  • ScruffyDuck
    ScruffyDuck over 11 years
    Thank you very much - so simple
  • wal
    wal over 10 years
    @RenniePet its my site, down for maintenance, bad timing!
  • Valentin Kuzub
    Valentin Kuzub over 9 years
    @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
  • wal
    wal over 9 years
    @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 ??
  • Rush Frisby
    Rush Frisby over 9 years
    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?
  • wal
    wal over 9 years
    @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
    wal over 9 years
    @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 <plug> see my article 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!
  • BaltoStar
    BaltoStar over 7 years
    I don't think this will work because SubClass1 will have two properties defined as ProtoMember with index=1 ( Name and BaseName )