使用protobuf-net时“没有为类型定义序列化程序:System.Collections.HashTable”或“System.ComponentModel.ISite”

时间:2017-04-03 20:20:05

标签: c# protobuf-net

尝试在protobuf-net中序列化类时遇到以下错误。 Hashtable包含许多不同类型的对象数组,所以很遗憾我不能使用字典。有一些派生类型的ISite,但如果界面本身是.NET的一部分,那么据我所知,我无能为力。

我已经查找了一些关于RuntimeTypeModel的内容,但据我所知,它只适用于您自定义的类。任何指导将非常感谢!感谢。

1 个答案:

答案 0 :(得分:1)

Hashtable不能正常运作的原因是不知道您将要加入的内容。 protobuf-net是一个基于合同的序列化器; Hashtable基本上是object - 到 - object的地图。 object 合同 - 实际上,它与合同完全相反。相比之下,Dictionary<int, SomeType>可以正常工作 (只要SomeType可以用作合同)。

ISite无法正常工作的原因是可能它是一个界面。接口代表实现,而不是数据。碰巧的是,protobuf-net 确实对接口成员提供了一些有限的支持,但坦率地说,你最好不要让接口远离DTO(即你的数据合同)。它应该创建什么类型的ISite实现?那需要什么数据?您使用的SomeFunSite : ISite如何被实例化并连接到事物上?序列化程序涉及的问题太多。序列化程序需要的是:&#34;我序列化Foo; Foo有2个整数,一个字符串和一个Bar - 尽管在某些情况下它还需要了解SuperBar : Bar子类&#34;。这已经绰绰有余了。

好合同:

[ProtoContract]
public class MyData {
    [ProtoMember(1)]
    public Dictionary<int, SomeType> Items {get; } = new Dictionary<int, SomeType>();

    [ProtoMember(2)]
    public SomethingElse Whatever { get;set;}
}

合同不好:

[ProtoContract]
public class MyData {
    [ProtoMember(1)]
    public Hashtable Items {get; } = new Hashtable();

    [ProtoMember(2)]
    public ISometing Whatever { get;set;}
}

在某些情况下可能会配置RuntimeTypeModel以了解您要执行的操作,但是:并非总是如此。这取决于背景;我没有的背景。

编辑:次要澄清:{get;} - 源代码现在只支持属性,但当前的NuGet版本不支持 - 基本上,暂时不使用它!

这是标记存储类似数据的可运行示例:

using ProtoBuf;
using System;
using System.Collections.Generic;

static class Program
{
    static void Main()
    {
        var obj = new MyData
        {
            Site = new BasicSite { BaseHost = "http://somesite.org" },
            Items =
            {
                {"key 1", SomeType.Create(123) },
                {"key 2", SomeType.Create("abc") },
                {"key 3", SomeType.Create(new Whatever { Id = 456, Name = "def" }) },
            }
        };

        var clone = Serializer.DeepClone(obj);
        Console.WriteLine($"Site: {clone.Site}");
        foreach(var pair in clone.Items)
        {
            Console.WriteLine($"{pair.Key} = {pair.Value}");
        }
    }
}


[ProtoContract]
class MyData
{
    private readonly Dictionary<string, SomeType> _items
        = new Dictionary<string, SomeType>();
    [ProtoMember(1)]
    public Dictionary<string, SomeType> Items => _items;
    [ProtoMember(2)]
    public ISite Site { get; set; }
}

[ProtoContract]
[ProtoInclude(1, typeof(SomeType<int>))]
[ProtoInclude(2, typeof(SomeType<string>))]
[ProtoInclude(3, typeof(SomeType<Whatever>))]
abstract class SomeType
{
    public object Value { get { return UntypedValue; } set { UntypedValue = value; } }
    protected abstract object UntypedValue { get; set; }

    public static SomeType<T> Create<T>(T value) => new SomeType<T> { Value = value };
}
[ProtoContract]
class SomeType<T> : SomeType
{
    [ProtoMember(1)]
    public new T Value { get; set; }
    protected override object UntypedValue { get => Value; set => Value = (T)value; }
    public override string ToString() => Value?.ToString() ?? "";
}
[ProtoContract]
class Whatever
{
    [ProtoMember(1)]
    public int Id { get; set; }
    [ProtoMember(2)]
    public string Name { get; set; }
    public override string ToString() => $"{Id}, {Name}";
}
[ProtoContract]
[ProtoInclude(1, typeof(BasicSite))]
interface ISite
{
    void SomeMethod();
}
[ProtoContract]
class BasicSite : ISite
{
    void ISite.SomeMethod() { Console.WriteLine(BaseHost); }
    [ProtoMember(1)]
    public string BaseHost { get; set; }
    public override string ToString() => BaseHost;
}