【问题标题】:Serializing List<Dictionary<string,object>> with Protobuf-Net使用 Protobuf-Net 序列化 List<Dictionary<string,object>>
【发布时间】:2015-11-06 12:58:36
【问题描述】:

我有一个要序列化的字典列表,但出现以下错误:

不支持 System.NotSupportedExceptionNested 或锯齿状列表和数组

甚至可以序列化吗?我尝试将其推送到另一个类并有一个List&lt;Dto&gt;,其中Dto 类是DictionaryIEnumerable,字典中的对象是DynamicType,但它仍然没有不工作。 我是完全错误地处理这个问题还是我错过了一些微妙的东西?

示例代码:

var zone = DateTimeZoneProviders.Tzdb["Europe/London"];
        _testObj = new Person
        {
            Name = "Homer",
            Age = 38,
            Timestamp = new ZonedDateTime(Instant.FromUtc(2013, 6, 12, 17, 53, 23), zone),
            Target = new DataCollection(new Dictionary<string, Type>()
            {
                {"Date", typeof(DateTime)}

            }, new List<Dictionary<string, object>>
            {
                new Dictionary<string, object>
                {
                    {"Date", new DateTime(2015, 1, 1)}
                }
            }
            , new List<string> { "Date" })

还有DataCollection

[Serializable, ProtoContract(IgnoreListHandling = true), DataContract, JsonObject]
public class DataCollection : IEnumerable<IDictionary<string, object>>
{
    public DataCollection(Dictionary<string, Type> fields, List<Dictionary<string, object>> data, List<string> keyFields)
    {
        Fields = fields;
        KeyFields = keyFields;
        Data = data;
    }

    public DataCollection()
    {
        Fields = new Dictionary<string, Type>();
        Data = new List<Dictionary<string, object>>();
        KeyFields = new List<string>();
    }

    [ProtoMember(1), DataMember]
    public Dictionary<string, Type> Fields { get; set; }

    [ProtoMember(2), DataMember]
    public List<string> KeyFields { get; set; }

    [ProtoMember(3), DataMember]
    public List<Dictionary<string, object>> Data { get; set; }

    public void Add(Dictionary<string, object> value)
    {
        Data.Add(value);
    }

    public IEnumerator<IDictionary<string, object>> GetEnumerator()
    {
        return Data.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

如果我不包含 ProtoMember(3),我可以很好地序列化。

【问题讨论】:

  • 我应该提到我已经成功地使用了一个代理来序列化 NodaTime 属性,没有任何问题。

标签: c# serialization protobuf-net


【解决方案1】:

所以问题归结为 Protobuf-net,这是正确的,不喜欢字典中的对象类型。我不会详细说明为什么我需要它,我只想说你需要使用 2 个代理类的组合,并且对象类型需要用 BinaryFormatter 序列化为 byte[] 然后发送到Protobuf-net,因为 Protobuf 规范不会承认对象类型。

我从这个网站复制解决方案的灵感:http://rushfrisby.com/serializing-non-serializable-types-with-protobuf/

根据发布解决方案的规则,而不是单独链接到外部网站:


以这个类为例:

[DataContract]
public class MyNameValueInfo
{
    [DataMember(Order = 1)]
    public string Name { get; set; }

    [DataMember(Order = 2)]
    public object Value { get; set; }
}

MyNameValueInfo 无法序列化,因为它不知道如何序列化 Value 属性(typeof 对象)。它会抛出一个异常:“No Serializer defined for type: System.Object”

为了解决这个问题,我们需要为 MyNameValueInfo 提供一个 protobuf-net 可以序列化的代理项。先注册代理类型(只需要做一次):

RuntimeTypeModel.Default.Add(typeof(MyNameValueInfo), false).SetSurrogate(typeof(MyNameValueInfoSurrogate));

然后实现 MyNameValueInfoSurrogate 以便它可以从/到 MyNameValueInfo 转换并且可以被 protobuf-net 序列化:

[DataContract]
public class MyNameValueInfoSurrogate 
{
        //string is serializable so we'll just copy this property back and forth
    [DataMember(Order = 1)]
    public string Name { get; set; } 

        //byte[] is serializable so we'll need to convert object to byte[] and back again
    [DataMember(Order = 2)]
    public byte[] Value { get; set; }

    public static implicit operator MyNameValueInfo(MyNameValueInfoSuggorage suggorage)
    {
        return suggorage == null ? null : new MyNameValueInfo
        {
            Name = suggorage.Name,
            Value = Deserialize(suggorage.Value)
        };
    }

    public static implicit operator MyNameValueInfoSuggorage(MyNameValueInfo source)
    {
        return source == null ? null : new MyNameValueInfoSuggorage
        {
            Name = source.Name,
            Value = Serialize(source.Value)
        };
    }

    private static byte[] Serialize(object o)
    {
        if (o == null)
            return null;

        using (var ms = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(ms, o);
            return ms.ToArray();
        }
    }

    private static object Deserialize(byte[] b)
    {
        if (b == null)
            return null;

        using (var ms = new MemoryStream(b))
        {
            var formatter = new BinaryFormatter();
            return formatter.Deserialize(ms);
        }
    }
}

【讨论】:

    猜你喜欢
    • 2011-10-17
    • 2013-01-16
    • 1970-01-01
    • 2021-10-20
    • 1970-01-01
    • 1970-01-01
    • 2017-06-12
    • 1970-01-01
    • 2018-01-25
    相关资源
    最近更新 更多