[英]Protobuf-net: How to serialize complex collection?
我正在尝试使用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<object>
因为我在我的代码中存储了其他类的不同类实例。
但我收到此错误消息:
Unable to resolve a suitable Add method for System.Collections.Generic.Dictionary...
这显然是因为字典,但我找不到解决方案如何解决这个问题。
您的基本问题是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
的限制:
DynamicType
- 使用Type
存储其他Type
信息(默认情况下,它包括AssemblyQualifiedName
,尽管这可以由用户控制)。 这使得序列化弱模型成为可能,即object
用于属性成员,但是目前这仅限于契约类型(不是原语),并且不适用于具有继承的类型(这些限制可能会在以后删除) 。 与AsReference
一样,它使用非常不同的布局格式
虽然上述文档存在于以前的项目站点并且尚未移至当前站点 ,但从版本2.0.0.668开始,对非合同类型的限制仍然存在。 (我测试过将一个int
值添加到List<object>
失败;我还没有检查对继承的限制是否仍然存在。)
在dbc的帮助下,以及他的回答和评论中提到的所有链接
[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子类型的InvalidOperationException
:ObjectWrapper`1 [[NewType]] 。
[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()));
} } } }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.