protobuf-net并使用接口序列化链表 [英] protobuf-net and serializing a linked list using interfaces

查看:118
本文介绍了protobuf-net并使用接口序列化链表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

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

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.

欢呼

亚历克斯

推荐答案

在实现作为合同接口的 top 对象时,需要完成一些出色的工作来改善对它们的处理. .基本上,目前,它仅处理根对象的非接口部分,但处理属性/子对象等的接口.

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.

您看到的异常是轻微奇数-由于subfoo属性,我希望它能及时了解ifoo,但是可以通过以下方式避免出现基本问题:添加:

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).

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

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.

说:将成员级别的属性从具体类型中删除,但是根对象的故障会使此问题变得有些麻烦.解决两者问题(不再需要PrepareSerializer)的另一种解决方法是:

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; }
}

当根对象/接口支持故障解决后,就不需要FooRoot包装器了,但是我可能需要添加一些开关以启用/禁用此修复程序,以实现传统支持.

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.

这篇关于protobuf-net并使用接口序列化链表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆