protobuf-net:反序列化 Guid 属性的错误线型异常

Posted

技术标签:

【中文标题】protobuf-net:反序列化 Guid 属性的错误线型异常【英文标题】:protobuf-net: incorrect wire-type exception deserializing Guid properties 【发布时间】:2010-04-10 05:28:48 【问题描述】:

我在使用 protobuf-net 反序列化 ORM 生成的实体的某些 Guid 属性时遇到问题。

这是代码的简化示例(重现了场景的大部分元素,但没有重现行为;我无法公开我们的内部实体,因此我正在寻找解释异常的线索)。假设我有一堂课,Account 有一个 AccountID 只读 guid 和一个 AccountName 读写字符串。我序列化并立即反序列化一个克隆。

反序列化时会引发Incorrect wire-type deserializing Guid 异常。

这里是示例用法...

        Account acct = new Account()  AccountName = "Bob's Checking" ;
        Debug.WriteLine(acct.AccountID.ToString());
        using (MemoryStream ms = new MemoryStream())
        
            ProtoBuf.Serializer.Serialize<Account>(ms, acct);
            Debug.WriteLine(Encoding.UTF8.GetString(ms.GetBuffer()));
            ms.Position = 0;
            Account clone = ProtoBuf.Serializer.Deserialize<Account>(ms);
            Debug.WriteLine(clone.AccountID.ToString());
        

这是一个 ORM 类的示例(经过简化,但演示了我能想到的相关语义)。使用 shell 游戏通过暴露支持字段来反序列化只读属性(“不能写”本质上变成“不应该写”,但我们可以扫描代码以获取分配给这些字段的实例,因此该 hack 适用于我们的目的)。

同样,这不会重现异常行为;我正在寻找关于可能的线索:

[DataContract()]
[Serializable()]
public partial class Account

    public Account()
    
        _accountID = Guid.NewGuid();
    
    [XmlAttribute("AccountID")]
    [DataMember(Name = "AccountID", Order = 1)]
    public Guid _accountID;

    /// <summary>
    /// A read-only property; XML, JSON and DataContract serializers all seem
    /// to correctly recognize the public backing field when deserializing: 
    /// </summary>
    [IgnoreDataMember]
    [XmlIgnore]
    public Guid AccountID
    
        get  return this._accountID; 
    

    [IgnoreDataMember]
    protected string _accountName;

    [DataMember(Name = "AccountName", Order = 2)]
    [XmlAttribute]
    public string AccountName
    
        get  return this._accountName; 
        set  this._accountName = value; 
    

XML、JSON 和 DataContract 序列化器似乎都可以很好地序列化/反序列化这些对象图,因此属性排列基本有效。我已经尝试过使用列表与单个实例、不同前缀样式等的 protobuf-net,但在反序列化时仍然总是得到“不正确的线型 ... Guid”异常。

所以具体的问题是,是否有任何已知的解释/解决方法?我不知道是什么情况(在实际代码中但不是示例中)可能导致它。

我们希望不必直接在实体层创建protobuf依赖;如果是这种情况,我们可能会创建代理 DTO 实体,其中所有公共属性都具有 protobuf 属性。 (这是我对所有声明性序列化模型的主观问题;这是一种普遍存在的模式,我理解它为什么会出现,但是 IMO,如果我们可以将人送上月球,那么“正常”应该是拥有对象和序列化合同解耦。;-) )

谢谢!

【问题讨论】:

@Paul - 只是基于您的调试行的一个随机想法......您是否在任何时候将二进制文件视为字符串?那行不通:marcgravell.blogspot.com/2010/03/binary-data-and-strings.html @marc - 合理的检查,但不,字符串输出只是为了快速的视觉增量。答案线程中的其他 cmets。谢谢! 【参考方案1】:

同意,您不需要显式依赖 - DataMember 很好。并且 protobuf-net 使用相同的逻辑重新忽略等。您如何/在哪里存储数据?以我的经验,最常见的原因是人们正在用不同的数据覆盖缓冲区(或文件),而不是截断它(在流的末尾留下垃圾)as discussed here。这与您的场景有关吗?

【讨论】:

+1,绝对是一个很好的搜索曲目。尝试设置 ms.Capacity = Convert.ToInt32(ms.Length) (为什么 .Capacity 是 int 和 .Length 很长,顺便说一句?);仍然遇到相同的“不正确的线型反序列化 Guid”异常。 @Paul - 我没有写 MemoryStream ;-p 关于电线类型问题 - 有没有可重现的例子? @Paul - 重要的不是Capacity;它是Length。如果您要覆盖MemoryStream,您必须 截断它(之前或之后都没有关系)。问题(覆盖)是,如果你写第一个对象并且它是(比如)254字节,那么你将位置设置为0并覆盖第二个对象(比如)120字节,那么流是*still 254 字节长。最后 134 个字节现在是垃圾。请参阅引用问题中的SetLength() 点。 @Marc Gravell - 谢谢!好提示;是的,我确实确保我正在从只写入一次的 MemoryStream 中读取。根据您的提示,我认为将其 .Capacity 截断为 .Length 可能会有所不同,但不幸的是没有。 (实际上,由于您提到的问题,我几乎从不重复使用内存流进行写入,而且我绝对不会在可重现的场景中。) @Paul - 恐怕如果没有可重现的场景,我真的会很挣扎。另外 - 可能值得尝试 v2 代码(来自主干) - 还不是完全稳定的(并且没有正式发布),但如果它是一个错误,它可能已经修复v2.

以上是关于protobuf-net:反序列化 Guid 属性的错误线型异常的主要内容,如果未能解决你的问题,请参考以下文章

了解 Protobuf-net 属性的序列化和反序列化

我可以反序列化为 protobuf-net 中接口的只读属性吗?

是否可以在 Silverlight 中使用 protobuf-net 对私有属性进行(反)序列化?

带有枚举的 Protobuf-net 反序列化异常

protobuf-net 不使用私有设置器序列化 C# 属性

使用非标准构造函数序列化和反序列化对象 - protobuf-net