protobuf-net 并使用接口序列化链表

Posted

技术标签:

【中文标题】protobuf-net 并使用接口序列化链表【英文标题】:protobuf-net and serializing a linked list using interfaces 【发布时间】:2012-09-27 14:00:20 【问题描述】:

我遇到了 protobuf-net 的问题,并将其缩小到这个最简单的情况。 我想要一个链表类型结构,其中一个类具有相同类型的属性。当我序列化它时,它工作得很好。但是,如果类型是接口而不是类,我会收到以下错误: 一旦为 ConsoleApplication1.foo (ConsoleApplication1.ifoo) 生成了序列化程序,就无法更改类型

这是我必须生成此错误的代码:

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; 

我已尽我所能阅读了有关该主题的所有内容,但我看不出我做错了什么。我尝试将我的对象放在一个包装类中,因为它看起来类似于接口列表的问题(代码未显示),但这仍然没有帮助。

如果我将 subfoo 更改为 foo 类型,那么它可以正常工作,但在我更复杂的现实世界问题中,我宁愿坚持使用界面。我在这里做错了什么还是 protobuf-net 有问题?

非常感谢任何帮助。

干杯

亚历克斯

【问题讨论】:

【参考方案1】:

顶层对象实现一个作为契约的接口时,需要完成一些出色的工作来改进对它的处理。基本上,目前它只处理根对象的非接口部分,但处理属性/子对象等的接口。

您看到的异常有点奇怪 - 由于subfoo 属性,我希望它及时了解ifoo,但这里的基本问题可以通过以下方式避免添加:

Serializer.PrepareSerializer<ifoo>();

在序列化代码之前(最好是在很早的地方;您只需要调用一次)。

但是,我还应该注意,您实际上在这里进行了双重序列化:当序列化 ifoo subfoo 属性时,它将序列化来自接口 both 的修饰成员 (ifoo) 和具体类型(foo)。这些实际上是相同的值,所以这里有一些冗余。

说:从具体类型中去掉成员级别的属性,但是根对象故障使这有点问题。解决两个问题(不再需要PrepareSerializer)的另一个修复是:

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; 

解决根对象/接口支持故障后,将不再需要 FooRoot 包装器,但我可能需要添加一些开关来启用/禁用修复,以获得旧版支持。

【讨论】:

以上是关于protobuf-net 并使用接口序列化链表的主要内容,如果未能解决你的问题,请参考以下文章

我可以反序列化为 protobuf-net 中接口的只读属性吗?

protobuf-net 使用啥基本序列化器来输出字节数组?

使用 protobuf-net 反序列化字典

使用 Protobuf-Net 序列化未知子类型

使用 ProtoBuf-Net,如何(反)序列化多维数组?

使用 protobuf-net 反序列化 int& 类型