protobuf-net.2.1.0
我的理解是protobuf-net
完全基于接收方可用的信息来确定反序列化的消息契约 - 序列化的数据包本身不依赖于构造消息契约。具体来说,类成员属性指示数据类型和预期在数据包中找到的字段的顺序。
因此,由于发送方侧独立于接收方,因此如果字段数据和字段数据,则应该可以解释某种类型的任何序列化分组。订单匹配由接收方原型合同定义。
特别是关于继承,应该可以序列化基类型的对象并反序列化为子类型的对象 - 只要正确地标记了继承。
但是,对于简单的继承层次结构DerivedClass : BaseClass
,我发现如果我序列化为BaseClass
并反序列化为DerivedClass
,则返回的对象将为{{1}类型}。
以下是课程:
BaseClass
执行以下测试方法:
[ProtoBuf.ProtoInclude(1000, typeof(DerivedClass))]
[ProtoBuf.ProtoContract]
public class BaseClass
{
[ProtoBuf.ProtoMember(1, IsRequired = false, Name = @"Name", DataFormat = ProtoBuf.DataFormat.TwosComplement)]
public string Name { get; set; }
}
[ProtoBuf.ProtoContract]
public class DerivedClass : BaseClass
{
[ProtoBuf.ProtoMember(2, IsRequired = false, Name = @"Index", DataFormat = ProtoBuf.DataFormat.TwosComplement)]
public int Index { get; set; }
}
产生例外:
类型' System.InvalidCastException'的例外情况发生在 protobuf-net.dll但未在用户代码中处理
附加信息:无法投射类型的对象 ' protobuf_net.lib.ProtoClasses.SimpleBaseClass'输入 ' protobuf_net.lib.ProtoClasses.SimpleDerivedClass'
答案 0 :(得分:0)
这里似乎存在protobuf-net的限制或错误。 TypeModel.DeserializeCore()
的工作方式是找到 base 契约类型,开始反序列化为该类型,当遇到派生类型的标记时,切换到反序列化该类型。最后,构建并填充观察类型的对象,从而导致您看到的问题,因为从未观察到所需派生类型的标记。
幸运的是,有一个简单的解决方法:使用Serializer.Merge<T>()
将流合并到预先分配的DerivedType
实例中:
var baseObject = new BaseClass { Name = "BaseObject" };
using (var stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize(stream, baseObject);
Debug.WriteLine(stream.Length);
stream.Seek(0, SeekOrigin.Begin);
var derivedObjectOut = ProtoBuf.Serializer.Merge(stream, new DerivedClass());
}
它有一点代码味道,但修复了问题。
顺便说一句,在原始示例代码中,您尝试两次读取流而不重写。这也会引发类似的异常,因为第二次调用会遇到 no 标记。
<强>更新强>
如果您正在编写通用反序列化代码,则可以在继承层次结构中的某处测试是否存在ProtoIncludeAttribute
,并使用以下帮助程序方法调用Merge()
(如果存在):
public static class ProtobufExtensions
{
public static T DeserializeOrMerge<T>(Stream stream)
{
if (!typeof(T).IsValueType
&& typeof(T) != typeof(string)
// Test to make sure T has a public default constructor
&& typeof(T).GetConstructor(Type.EmptyTypes) != null
&& typeof(T).HasProtoIncludeAtributes())
{
return ProtoBuf.Serializer.Merge(stream, Activator.CreateInstance<T>());
}
else
{
return ProtoBuf.Serializer.Deserialize<T>(stream);
}
}
public static bool HasProtoIncludeAtributes(this Type type)
{
if (type == null)
throw new ArgumentNullException();
if (!type.IsDefined(typeof(ProtoContractAttribute)))
return false;
return type.BaseTypesAndSelf().SelectMany(t => t.GetCustomAttributes<ProtoIncludeAttribute>()).Any();
}
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
然后使用它:
var baseObject = new BaseClass { NameInBaseClass = "BaseObject" };
using (var stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize(stream, baseObject);
Debug.WriteLine(stream.Length);
stream.Seek(0, SeekOrigin.Begin);
var derivedObjectOut = ProtobufExtensions.DeserializeOrMerge<DerivedClass>(stream);
}