protobuf-net and serializing a linked list using interfaces

守給你的承諾、 提交于 2019-12-23 20:14:36

问题


I have come across a problem with protobuf-net and have narrowed it down to this simplest case. I want a linked list type structure, where a class has a property of the same type. When I serialize this it works great. However if the type is an interface instead of a class I get the following error: The type cannot be changed once a serializer has been generated for ConsoleApplication1.foo (ConsoleApplication1.ifoo)

This is the code I have to generate this error:

class Program
{
    static void Main(string[] args)
    {
        var toSerialize = new foo();
        toSerialize.data = "foo1";

        var subf = new foo();
        subf.data = "foo2";

        toSerialize.subfoo = subf;

        using (var pbfsc = new FileStream("testfile.proto", FileMode.Create))
        {
            using (var cs = new GZipStream(pbfsc, CompressionMode.Compress))
            {
                ProtoBuf.Serializer.Serialize(cs, toSerialize);
            }
            pbfsc.Close();
        }
    }
}

[ProtoContract, ProtoInclude(2000, typeof(foo))]
public interface ifoo
{
    [ProtoMember(1)]
    string data { get; set; }
    [ProtoMember(2)]
    ifoo subfoo { get; set; }
}
[ProtoContract]
public class foo : ifoo
{

    [ProtoMember(1)]
    public string data { get; set; }
    [ProtoMember(2)]
    public ifoo subfoo { get; set; }
}

I have done all the reading on the subject I can and I can't see what I have done wrong. I tried putting my object in a wrapper class as it looks similiar to a problem with lists of interfaces (code not shown) and this still didn't help.

If I change subfoo to type foo then it works fine, but in my more complex real world problem I would rather stick to using interface. Have I done something wrong here or is this a problem with protobuf-net?

Any help greatly appreciated.

Cheers

Alex


回答1:


There is some outstanding work that needs completing to improve the treatment of the top level object when it implements an interface that is a contract. Basically, at the moment it only processes the non-interface parts of the root object, but handles interfaces for properties / sub-objects etc.

The exception you are seeing is slightly odd - I would expect it to have known about ifoo in time, due to the subfoo property, but the fundamental problem here can be avoided by adding:

Serializer.PrepareSerializer<ifoo>();

before the serialization code (ideally somewhere pretty early; you only need to call it once).

However, I should also note that you're actually double-serializing here: when serializing the ifoo subfoo property, it will serialize the decorated members from both the interface (ifoo) and the concrete type (foo). These are actually the same values, so there is some redundancy here.

I would say: take the member-level attributes off the concrete type, but the root-object glitch makes this slightly problematic. Another fix, that solves both issues (no longer requiring the PrepareSerializer) is:

using ProtoBuf;
using System;
class Program
{
    static void Main(string[] args)
    {
        var root = new foo();
        root.data = "foo1";

        var subf = new foo();
        subf.data = "foo2";

        root.subfoo = subf;
        var toSerialize = new FooRoot { root = root };

        // this does the same as your file-code, but runs
        // both serialize and deserialize - basicaly, it is
        // a lazy way of checking it end-to-end
        var clone = Serializer.DeepClone(toSerialize).root;
        Console.WriteLine(clone.data); // "foo1"
        Console.WriteLine(clone.subfoo.data); // "foo2"
    }
}
[ProtoContract]
public class FooRoot
{
    [ProtoMember(1)]
    public ifoo root { get; set; }
}
[ProtoContract, ProtoInclude(2000, typeof(foo))]
public interface ifoo
{
    [ProtoMember(1)]
    string data { get; set; }
    [ProtoMember(2)]
    ifoo subfoo { get; set; }
}
[ProtoContract]
public class foo : ifoo
{
    public string data { get; set; }
    public ifoo subfoo { get; set; }
}

When the root-object/interface-support glitch is resolved, the FooRoot wrapper would not be needed, but I'll probably need to add some switch to enable/disable the fix, for legacy support.



来源:https://stackoverflow.com/questions/12623178/protobuf-net-and-serializing-a-linked-list-using-interfaces

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!