In this blog post, we will look into how to handle subclasses or interface implementations during protobuf serialization. Consider the following class.
[ProtoContract]
public class Person
{
[ProtoMember(1)]
public string FirstName { get; set; }
[ProtoMember(2)]
public string LastName { get; set; }
}
For serializing an instance of the above with protobuf-net, you could do it easily as the following.
public static byte[] SerializeObject<T>(T record)
{
using var stream = new MemoryStream();
Serializer.Serialize(stream, record);
return stream.ToArray();
}
However, let us now add some more properties to the class, which would make use of an abstraction or a sub-class. For example,
public interface ISubject
{
string Name { get; set; }
}
public class Subject : ISubject
{
public string Name { get; set; }
}
[ProtoContract]
public class Person
{
[ProtoMember(1)]
public string FirstName { get; set; }
[ProtoMember(2)]
public string LastName { get; set; }
[ProtoMember(3)]
public IEnumerable<ISubject> Subjects { get; set; }
}
The above code ould fail to serialize as there is a lack of sufficient information regarding the types involved in Subjects. The fix is easy. You could make use of the ProtoInclude
for mentioning the possible subtypes.
[ProtoContract]
[ProtoInclude(100,typeof(Subject))]
public interface ISubject
{
[ProtoMember(1)]
string Name { get; set; }
}
public class Subject : ISubject
{
public string Name { get; set; }
}
The ProtoInclude
attribute is a bit like the XmlInclude
attribute for the XmlSerializer
– it allows you to explicitly declare the subtypes for each type, which would be used during the (de)serialization.
That’s all for now. As a side note, I wanted to talk about the AsReference
property of the ProtoMember
attribute, but as of v3.xx, the property has been marked as Obsolete. So until there is an alternative for the same, we will delay the discussion on the same.