Christian Becker
Christian Becker

Reputation: 31

Serialize base class only in ProtoBuf-net

We have a base class and derived class. When we serialize a derived class, we want only the base class to be serialized. How can we achieve this in ProtoBuf-net.

Upvotes: 1

Views: 257

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062502

Edit: new things in v3; the following now works:

[ProtoContract(IgnoreUnknownSubTypes = true)]
public class Foo {}

which does exactly what you want here.


Currently protobuf-net is not very forgiving of unexpected types, because it really wants to be able to round-trip your data. In the 3.0 codebase, this is currently restricted to:

  • EF proxies, which it detects by the namespace System.Data.Entity.DynamicProxies
  • NHibernate proxies, which it detects as anything that implements by name NHibernate.Proxy.INHibernateProxy, NHibernate.Proxy.DynamicProxy.IProxy or NHibernate.Intercept.IFieldInterceptorAccessor

I'm open to discussing new additions that could be added to relax this - it would probably need a little discussion, but: it should be possible. Simply adding a marker interface or attribute that the library recognizes as a "ignore this type" token seems pretty reasonable! In fact, as I think about it: it would seem that we could just use [ProtoIgnore] for this, by extending the AttributeTargets for ProtoContractAttribute, and adding a few lines to DynamicStub.ResolveProxies.

Right now, the following "works" (for limited values of "works"), with outputs:

Trying BaseType...
Success; got BaseType
Trying KnownSubTypeViaInclude...
Success; got KnownSubTypeViaInclude
Trying UnknownSubType...
Unexpected sub-type: UnknownSubType
Trying LooksLikeEFProxy...
Success; got BaseType
Trying LooksLikeNHibernateProxy...
Success; got BaseType

code:

using ProtoBuf;
using System;

static class P
{
    static void Main()
    {
        // works, trivially
        Try<BaseType>();

        // works, processes sub-type
        Try<KnownSubTypeViaInclude>(); 

        // fails, unexpected
        Try<UnknownSubType>();

        // works, processes base type only
        Try<System.Data.Entity.DynamicProxies.LooksLikeEFProxy>();
        Try<LooksLikeNHibernateProxy>();
    }
    static void Try<T>() where T : BaseType, new()
    {
        Console.WriteLine($"Trying {typeof(T).Name}...");
        try
        {
            var clone = Serializer.DeepClone<BaseType>(new T());
            Console.WriteLine($"Success; got {clone.GetType().Name}");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

[ProtoContract]
[ProtoInclude(42, typeof(KnownSubTypeViaInclude))]
public class BaseType
{

}
public class KnownSubTypeViaInclude : BaseType { }
public class UnknownSubType : BaseType { }
public class LooksLikeNHibernateProxy : BaseType, NHibernate.Proxy.INHibernateProxy { }
namespace System.Data.Entity.DynamicProxies
{
    public class LooksLikeEFProxy : BaseType { }
}
namespace NHibernate.Proxy // let's pretent to be NHibernate
{
    public interface INHibernateProxy { }
}

Upvotes: 1

Related Questions