【问题标题】:Protobuf-net: How to serialize complex collection?Protobuf-net:如何序列化复杂的集合?
【发布时间】:2016-08-29 14:14:43
【问题描述】:

我正在尝试使用 protobuf-net 序列化此类对象:

[ProtoContract]
public class RedisDataObject
{
    [ProtoMember(1)]
    public string DataHash;
    [ProtoMember(2, DynamicType = true)]
    public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
}

[Serializable]
public enum ContextActions
{
    Insert,
    Update,
    Delete
}

我使用 List&lt;object&gt; 是因为我在代码中存储了其他类的不同类实例。

但我收到此错误消息:

Unable to resolve a suitable Add method for System.Collections.Generic.Dictionary...

这显然是因为字典,但我找不到如何解决这个问题的解决方案。

【问题讨论】:

    标签: c# serialization protobuf-net


    【解决方案1】:

    您的基本问题是 DynamicType = true 仅适用于该特定属性,并且仅序列化该特定属性的值的类型信息。它不适用于该对象包含的任何属性。但是,您的 object 值嵌套在多个级别的容器中:

    public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
    

    您需要做的是序列化此列表元组字典中每个object 的类型信息。您可以通过引入代理值类型来做到这一点:

    [ProtoContract]
    public struct DynamicTypeSurrogate<T>
    {
        [ProtoMember(1, DynamicType = true)]
        public T Value { get; set; }
    }
    
    public static class DynamicTypeSurrogateExtensions
    {
        public static List<DynamicTypeSurrogate<T>> ToSurrogateList<T>(this IList<T> list)
        {
            if (list == null)
                return null;
            return list.Select(i => new DynamicTypeSurrogate<T> { Value = i }).ToList();
        }
    
        public static List<T> FromSurrogateList<T>(this IList<DynamicTypeSurrogate<T>> list)
        {
            if (list == null)
                return null;
            return list.Select(i => i.Value).ToList();
        }
    }
    

    然后修改您的RedisDataObject 以序列化代理字典,如下所示:

    [ProtoContract]
    public class RedisDataObject
    {
        [ProtoMember(1)]
        public string DataHash;
    
        [ProtoIgnore]
        public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
    
        [ProtoMember(2)]
        private Dictionary<ContextActions, List<Tuple<string, List<DynamicTypeSurrogate<object>>>>> SurrogateValue
        {
            get
            {
                if (Value == null)
                    return null;
                var dictionary = Value.ToDictionary(
                    p => p.Key,
                    p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.ToSurrogateList())).ToList()));
                return dictionary;
            }
            set
            {
                if (value == null)
                    Value = null;
                else
                {
                    Value = value.ToDictionary(
                        p => p.Key,
                        p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.FromSurrogateList())).ToList()));
                }
            }
        }
    }
    

    还要注意DynamicType 提到的here 的限制:

    DynamicType - 使用类型存储附加的Type 信息(默认情况下它包括AssemblyQualifiedName,尽管这可以由用户控制)。这使得序列化弱模型成为可能,即 object 用于属性成员,但是目前这仅限于 contract 类型(不是原语),并且不适用于具有继承的类型(这些限制可能会在以后删除)。与AsReference 一样,它使用了非常不同的布局格式

    虽然上面的文档存在于former project site 并且尚未移动到current site,但对于非合约类型的限制在版本 2.0.0.668 中肯定仍然存在。 (我测试了在List&lt;object&gt;中添加int值失败;我没有检查继承的限制是否仍然存在。)

    【讨论】:

    • 我收到 InvalidOperationException动态类型不是合约类型:,包含双精度、小数、日期和字符串的对象列表。
    • @andrei.ciprian - 这听起来像this question 中描述的问题。
    • 我喜欢你的方法,但它对任何类型都不起作用。你建议的问题我想会,as this one would
    • @andrei.ciprian - 当每个对象是原始问题中提到的 其他类的类实例 时,它可以工作 - 即映射到数据合同类型的一些 POCO .我测试了几个示例[ProtoContract] 类。但也许我可以将此与here 的答案结合起来,并取消对非合同类型的限制。
    • @andrei.ciprian,您需要将属性 [ProtoContract] 添加到您的班级。看看here 看看如何设置它。基本上,您需要将属性 [ProtoMember(N)] 添加到要使用的字段中,或者只使用 ImplicitFields 注释,如下所示:[ProtoContract(ImplicitFields = ImplicitFields.AllFields)] 包含类中的所有字段。这个注解ImplicitFields有更多的选择,你可以根据需要去探索和使用
    【解决方案2】:

    dbc 的帮助下,以及他的回答和 cmets 中提到的所有链接

    [ProtoContract]
    [ProtoInclude(1, typeof(ObjectWrapper<int>))]
    [ProtoInclude(2, typeof(ObjectWrapper<decimal>))]
    [ProtoInclude(3, typeof(ObjectWrapper<DateTime>))]
    [ProtoInclude(4, typeof(ObjectWrapper<string>))]
    [ProtoInclude(5, typeof(ObjectWrapper<double>))]
    [ProtoInclude(6, typeof(ObjectWrapper<long>))] 
    [ProtoInclude(8, typeof(ObjectWrapper<Custom>))]
    [ProtoInclude(9, typeof(ObjectWrapper<CustomType[]>))]
    public abstract class ObjectWrapper
    {
      protected ObjectWrapper() { }
      abstract public object ObjectValue { get; set; }
    
      public static ObjectWrapper Create(object o) {
        Type objectType = o.GetType();
        Type genericType = typeof(ObjectWrapper<>);
        Type specializedType = genericType.MakeGenericType(objectType);
        return (ObjectWrapper)Activator.CreateInstance(specializedType, new object[] { o });
      }
    }
    

    缺点是您必须在对象列表中注册您正在使用的所有类型。每次出现ProtoInclude 系列中未包含的新类型时,您都会收到带有消息Unexpected sub-type: ObjectWrapper`1[[NewType]]InvalidOperationException。。 p>

    [ProtoContract]
    public class RedisDataObjectWrapper {
      [ProtoMember(1)] public string DataHash;
      [ProtoIgnore] public Dictionary<ContextActions, List<Tuple<string, List<object>>>> Value;
    
    [ProtoMember(2)] 
    private Dictionary<ContextActions, List<Tuple<string, List<ObjectWrapper>>>> AdaptedValue {
      get {
        if (Value == null) return null;
        var dictionary = Value.ToDictionary(
            p => p.Key,
            p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.Select(x=>ObjectWrapper.Create(x)).ToList() )).ToList()));
        return dictionary;
      }
      set {
        if (value == null) Value = null;
        else {
          Value = value.ToDictionary(
              p => p.Key,
              p => (p.Value == null ? null : p.Value.Select(i => Tuple.Create(i.Item1, i.Item2.Select(x=>x.ObjectValue).ToList() )).ToList()));
        } } } }
    

    【讨论】:

    • 这个解决方案对我很有效!对于所有使用此解决方案的人,您需要创建一个 ObjectWrapper 类。这是我使用的实现。 [ProtoContract] 公共类 ObjectWrapper : ObjectWrapper { public ObjectWrapper() :base(){ } public ObjectWrapper(object val) :this() { ObjectValue = val; } 公共覆盖对象 ObjectValue { 获取 => 值;设置 => 值 = (T) 值; } [ProtoMember(1)] 公共 T 值 { 获取;放; } }
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-16
    相关资源
    最近更新 更多