开发者

Protobuf Inheritance and Generics

I am attempting to use ProtoBuf net to serialize an object tree with the classes in the following format:

[ProtoContract]
class MySpecialCollectionList<T> : List<MySpecialCollection<T>>
{
    [ProtoMember(1)]
    public string Name { get; set; }
}

[ProtoContract]
class MySpecialCollection<T> : List<Special<T>>
{
    [ProtoMember(1)]
    public string Name { get; set; }
}

[ProtoContract]
class Special<T>
{
    [ProtoMember(1)]
    public string Name { get; set; }
    [ProtoMember(2)]
    public string Description { get; set; }

    [ProtoMember(3)]
    private readonly T _source; 
    T Source { get { return _source; } }

    private Special()
    {
    }

    public Special(T source) 
    { 
        _source = source; 
    }
}

interface IBeast
{
    string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
    [ProtoMember(1)]
    public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
    [ProtoMember(1)]
    public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
    [ProtoMember(1)]
    public string Name { get; set; }
}

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
        using (var fs = File.Create(@"c:\temp\protobuftest.bin"))
        {
            Serializer.Serialize(fs, collectionList);

            fs.Close();
        }
    }

    private MySpecialCollectionList<IBeast> GetSpecialCollectionList()
    {
        var ant = new Ant() { Name = "Mr Ant" };
        var cat = new Cat() { Name = "Mr Cat" };
        var dog = new Dog() { Name = "Mr Dog" };

        var Special = new Special<IBeast>(ant);

        var specialCollection1 = new MySpecialCollection<IBeast>() {
            {new Special<IBeast>(ant)},
            {new Special<IBeast>(cat)},
            {new Special<IBeast>(dog)}
        };
        specialCollection1.Name = "Special Collection1";


        var specialCollection2 = new MySpecialCollection<IBeast>() {
            {new Special<IBeast>(ant)},
            {new Special<IBeast>(dog)}
        };
        specialCollection2.Name = "Speci开发者_如何学Pythonal Collection2";

        var specialCollectionList = new MySpecialCollectionList<IBeast>() {
            specialCollection1, specialCollection2 };

        specialCollectionList.Name = "Special Collection List";
        return specialCollectionList;
    }
}

Notice how the class I am serializing (MySpecialCollectionList<T>) is derived from a List<SomeOtherClass<T>>, not just List<T>.

I am struggling to work out where to put "ProtoInclude" attributes to get this to serialize all the items in the MySpecialCollectionList. Any help would be much appreciated.


Inheritance is not an issue here since even if A : B it is not true that Foo<A> : Foo<B>. Note that protobuf-net won't use a non-default constructor, although it is possible to skip the constructor, binding to the field directly (even readonly). While you may have 6 T, I can't see (from the code) that it would ever be in doubt which closed type you intend, and if the closed type is known you should be set.

If you have a Foo<SomeBaseClass> and a number of concrete types inherited from SomeBaseClass then the markers would o on SomeBaseClass.

However, if you have a concrete scenario I can use to reproduce your issue, I'll happily take a look.


Updated re edit:

There are a couple of key points drawn out in the example:

  • in common with most binding APIs, XmlSerializer and IIRC DataContractSerializer, an item is either a list xor an item with values; if a collection (something implementing IList) has properties itself, they will not be serialized; encapsulation is preferred over inheritance here, i.e. something that has a Name and has a list (rather than has a Name and is a list)
  • protobuf-net v1 does not support interface-based serialization; v2 does, but as with XmlSerializer and DataContractSerializer you need to explicitly tell it what things it needs to expect; quite nicely, though, we can move the [ProtoMember] onto the interface itself

Here's a fully working version in v2:

using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
class MySpecialCollectionList<T>
{
    [ProtoMember(1)]
    public string Name { get; set; }

    private readonly List<MySpecialCollection<T>> items = new List<MySpecialCollection<T>>();
    [ProtoMember(2)]
    public List<MySpecialCollection<T>> Items { get { return items; } }
}

[ProtoContract]
class MySpecialCollection<T>
{
    [ProtoMember(1)]
    public string Name { get; set; }

    private readonly List<Special<T>> items = new List<Special<T>>();
    [ProtoMember(2)]
    public List<Special<T>> Items { get { return items; } }
}

[ProtoContract]
class Special<T>
{
    [ProtoMember(1)]
    public string Name { get; set; }
    [ProtoMember(2)]
    public string Description { get; set; }

    [ProtoMember(3)]
    private readonly T _source;
    T Source { get { return _source; } }

    private Special()
    {
    }

    public Special(T source)
    {
        _source = source;
    }
}
[ProtoContract]
[ProtoInclude(2, typeof(Ant))]
[ProtoInclude(3, typeof(Cat))]
[ProtoInclude(4, typeof(Dog))]
interface IBeast
{
    [ProtoMember(1)]
    string Name { get; set; }
}
[ProtoContract]
class Ant : IBeast
{
    public string Name { get; set; }
}
[ProtoContract]
class Cat : IBeast
{
    public string Name { get; set; }
}
[ProtoContract]
class Dog : IBeast
{
    public string Name { get; set; }
}

public static class Form1
{

    private static void Main()
    {
        MySpecialCollectionList<IBeast> collectionList = GetSpecialCollectionList();
        var copy = Serializer.DeepClone(collectionList);
    }

    private static MySpecialCollectionList<IBeast> GetSpecialCollectionList()
    {
        var ant = new Ant() { Name = "Mr Ant" };
        var cat = new Cat() { Name = "Mr Cat" };
        var dog = new Dog() { Name = "Mr Dog" };

        var Special = new Special<IBeast>(ant);

        var specialCollection1 = new MySpecialCollection<IBeast>() {Items =
            {new Special<IBeast>(ant),
            new Special<IBeast>(cat),
            new Special<IBeast>(dog)}
        };
        specialCollection1.Name = "Special Collection1";


        var specialCollection2 = new MySpecialCollection<IBeast>()
        {
            Items =
            {new Special<IBeast>(ant),
            new Special<IBeast>(dog)}
        };
        specialCollection2.Name = "Special Collection2";

        var specialCollectionList = new MySpecialCollectionList<IBeast>()
        {
            Items ={
            specialCollection1, specialCollection2 }
        };

        specialCollectionList.Name = "Special Collection List";
        return specialCollectionList;
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜