Protobuf-net 序列化错误 =“一旦生成了序列化程序,就无法更改类型”

Posted

技术标签:

【中文标题】Protobuf-net 序列化错误 =“一旦生成了序列化程序,就无法更改类型”【英文标题】:Protobuf-net serialization error = "The type cannot be changed once a serializer has been generated" 【发布时间】:2011-09-07 11:40:07 【问题描述】:

以下场景似乎导致 Protobuf.net 中反序列化异常。我做错了什么吗?有没有办法解决这个问题?

[ProtoContract]
[ProtoInclude(2, typeof(Ant))]
[ProtoInclude(3, typeof(Cat))]
public interface IBeast

    [ProtoMember(1)]
    string Name  get; set; 


[ProtoContract]
public class Ant : IBeast

    public string Name  get; set; 


[ProtoContract]
public class Cat : IBeast

    public string Name  get; set; 


[ProtoContract]
[ProtoInclude(1, typeof(AntRule1))]
[ProtoInclude(2, typeof(AntRule2))]
[ProtoInclude(3, typeof(CatRule1))]
[ProtoInclude(4, typeof(CatRule2))]
public interface IRule<T> where T : IBeast

    bool IsHappy(T beast);


[ProtoContract]
public class AntRule1 : IRule<Ant>

    public bool IsHappy(IAnt beast)
    
        return true;
    


[ProtoContract]
public class AntRule2 : IRule<Ant>

    public bool IsHappy(IAnt beast)
    
        return true;
    


[ProtoContract]
public class CatRule1 : IRule<Cat>

    public bool IsHappy(ICat beast)
    
        return true;
    


[ProtoContract]
public class CatRule2 : IRule<Cat>

    public bool IsHappy(ICat beast)
    
        return true;
    


public class TestSerialization

    public void Serialize()
    
        var antRules = new List<IRule<Ant>>();
        antRules.Add(new AntRule1());
        antRules.Add(new AntRule2());

        var catRules = new List<IRule<Cat>>();
        catRules.Add(new CatRule1());
        catRules.Add(new CatRule2());

        using (var fs = File.Create(@"c:\temp\antRules.bin"))
        
            ProtoBuf.Serializer.Serialize(fs, antRules);

            fs.Close();
        

        using (var fs = File.OpenRead(@"c:\temp\antRules.bin"))
        
            List<IRule<Ant>> list;
            list = ProtoBuf.Serializer.Deserialize<List<IRule<Ant>>>(fs);

            fs.Close();
        

        using (var fs = File.Create(@"c:\temp\catRules.bin"))
        
            ProtoBuf.Serializer.Serialize(fs, catRules);

            fs.Close();
        

        using (var fs = File.OpenRead(@"c:\temp\catRules.bin"))
        
            List<IRule<Cat>> list;
            list = ProtoBuf.Serializer.Deserialize<List<IRule<Cat>>>(fs);

            fs.Close();
        
    

【问题讨论】:

我很高兴看到,但我在编译您的示例时遇到了困难;添加缺少的ICat/IAnt 后仍然有一些构建失败。如果我玩得太多,我不知道我在看同样的问题......你能发布一个编译的例子吗? 其实,我认为我有repro...看着 啊,对不起,我在编辑和剪切和粘贴的过程中一定丢失了一些东西。如果您需要我重新复制代码,请告诉我。 抱歉 - IAnt 和 ICat 不应该存在。规则应该是这样的...... public class AntRule1 : IRule 认为我有足够的时间发布答案 - 无论哪种方式都让我知道。 【参考方案1】:

最终我怀疑这里的问题是:

    [ProtoContract]
    [ProtoInclude(1, typeof(AntRule1))]
    [ProtoInclude(2, typeof(AntRule2))]
    [ProtoInclude(3, typeof(CatRule1))]
    [ProtoInclude(4, typeof(CatRule2))]
    public interface IRule<T> where T : IBeast

这表示对于任何TIRule&lt;T&gt; 有 4 个孩子。这样做的副作用是说如果你有多个T,每个AndRule1...CatRule2 每个都有“n”个父母,这不好。让我们假设IRule&lt;Ant&gt; 有2 条蚂蚁规则,依此类推……(毕竟,我怀疑CatRule1 真的是IRule&lt;Ant&gt; 的实现)。目前这只能通过RuntimeTypeModel 表达,因为属性总是适用于all T:

[ProtoContract]
public interface IRule<T> where T : IBeast

// note these are unrelated networks, so we can use the same field-numbers
RuntimeTypeModel.Default[typeof(IRule<Ant>)]
    .AddSubType(1, typeof(AntRule1)).AddSubType(2, typeof(AntRule2));
RuntimeTypeModel.Default[typeof(IRule<Cat>)]
    .AddSubType(1, typeof(CatRule1)).AddSubType(2, typeof(CatRule2));

然后它就可以工作了。注意配置只需要进行一次,通常在应用启动时。


考虑一下,我可以可能只在运行时进行测试,对于泛型,只需忽略任何不适用的 - 我的意思是在评估 IRule&lt;Dog&gt; 时,只考虑特定类型,如果他们实现 IRule&lt;Dog&gt;。不过,我仍然有两种想法。

【讨论】:

另一个快速准确的响应。效果很好。再次感谢。 @MarcGravell 在使用RuntimeTypeModel 时,您是否还必须使用[ProtoContract] 属性(如果不需要,会使用该属性破坏任何东西)?

以上是关于Protobuf-net 序列化错误 =“一旦生成了序列化程序,就无法更改类型”的主要内容,如果未能解决你的问题,请参考以下文章

protobuf-net 反序列化错误 无效标签:0

protobuf-net:反序列化 Guid 属性的错误线型异常

Protobuf-net“反序列化期间引用跟踪对象更改引用”错误(2)

Protobuf-net 错误 - 序列化设置为 null 的固定数组

对象(通用)的 Protobuf-net 序列化抛出错误没有为类型定义序列化程序:System.Object

ProtoBuf-Net 错误消息“源数据中的无效字段:0”