ProtoBuf-Net 错误消息“源数据中的无效字段:0”

Posted

技术标签:

【中文标题】ProtoBuf-Net 错误消息“源数据中的无效字段:0”【英文标题】:ProtoBuf-Net error message " Invalid field in source data: 0" 【发布时间】:2013-01-07 12:59:29 【问题描述】:

我成功序列化以下类的实例,但是当我在收到以下错误消息后立即尝试反序列化时:" Invalid field in source data: 0"

我不知道它指的是什么,因为我直接找到了下面的类。我刚刚将 protobuf-net 版本更新为 2.00.614(运行时版本:2.0.50727)。

知道我是否可能忽略了一些琐碎的事情吗?

[ProtoContract]
public class TimeSeriesProperties 

    [ProtoMember(1)]
    public string TimeSeriesName  get; private set; 
    [ProtoMember(2)]
    public string FileName  get; private set; 
    [ProtoMember(3)]
    public string TemplateName  get; private set; 
    [ProtoMember(4)]
    public int PacketLength  get; private set; 
    [ProtoMember(5)]
    public long FileSizeBytes  get; set; 
    [ProtoMember(6)]
    public long NumberRecords  get; set; 
    [ProtoMember(7)]
    public DateTime DateTimeStart  get; set; 
    [ProtoMember(8)]
    public DateTime DateTimeEnd  get; set; 

    public TimeSeriesProperties()
    

    

    public TimeSeriesProperties(string timeSeriesName, string fileName, string templateName, int PacketLength)
    
        this.TimeSeriesName = timeSeriesName;
        this.FileName = fileName;
        this.TemplateName = templateName;
        this.PacketLength = PacketLength;
    



public static byte[] Serialize_ProtoBuf<T>(T serializeThis)
    
        using (var stream = new MemoryStream())
        
            ProtoBuf.Serializer.Serialize<T>(stream, serializeThis);
            return stream.GetBuffer();
        
    

    public static T Deserialize_ProtoBuf<T>(byte[] byteArray)
    
        using (var stream = new MemoryStream(byteArray))
        
            return ProtoBuf.Serializer.Deserialize<T>(stream);
        
    

【问题讨论】:

这几乎总是意味着您正在过度阅读写入的数据,通常是通过错误处理MemoryStream - 与protobuf-net无关;请问你能显示进行序列化/反序列化测试的代码吗? (理想情况下,添加一个@marc 评论,所以我知道回来看看) @MarcGravell,添加了这两个方法。 这种方法永远不会适用于 any protobuf 实现;最外面的消息不知道自己的长度是规范的一个特性——这是为了使片段可以通过连接进行合并。因此,默认情况下,它会读取到流的末尾(尽管大多数实现包括读取n 字节的机制,对于某些n)。在每个字段的末尾,它期望 要么 另一个字段头或 EOF。 0 从不是一个有效的字段头,所以尾随的零会破坏反序列化器。每一次。 顺便说一下,写入的数据量简直就是:stream.Length 这完全取决于上下文;在您展示的场景中(大多数Streams 等),长度很容易获得,整个事情只是通过ToArray() 或受约束的读取来固定。对于某些实现(例如 NetworkStream 发送多条消息),[Serialize|Deserialize]WithLengthPrefix 是您的朋友。我不确定为什么你不知道序列化后的长度。你能扩展一下吗?如果这是文件开头的标题,*WithLengthPrefix 应该可以正常工作。 【参考方案1】:

我见过的最常见的原因就是在MemoryStream 上错误地使用了GetBuffer()。当我添加评论时,这已经是我的预感,但你已经确认了:

using (var stream = new MemoryStream())

    ProtoBuf.Serializer.Serialize<T>(stream, serializeThis);
    return stream.GetBuffer();

GetBuffer 返回 oversized 后备缓冲区。它的末尾有垃圾。使用GetBuffer 完全没问题,只要你还记录了.Length,这是有效数据的数量。这可以避免对潜在的大数组进行额外分配。但在您的情况下,一种更简单的方法可能是使用 ToArray() 来获取 大小合适的 缓冲区:

using (var stream = new MemoryStream())

    ProtoBuf.Serializer.Serialize<T>(stream, serializeThis);
    return stream.ToArray();

【讨论】:

标记为已回答。对我来说,关键是“WithLengthPrefix”问题。我可以发誓这在没有之前就可以工作,我的代码在我更新到当前的 ProtoBuf-net 版本后就坏了。我可以向您保证,我序列化为大小为 256 的字节数组,即使实际序列化对象的长度仅为 146,并且我通过传入一个 10,000 字节数组进行反序列化,其中只有前 146 个字节被序列化对象占用。它奏效了,我对此深信不疑。我不知道发生了什么变化,但我知道我的代码不安全,因为序列化对象没有以大小为前缀。 如果您使用的是新存档,则此解决方案有效。如果我们已经使用 GetBuffer() 写出存档并且我们不再知道存档的大小,有没有办法解决这个问题? @Etienne 您可能会使用阅读器 API 来检查下一个字段标题何时为零,这绝不是合法的 - 仅当 MemoryStream 以前没有用于其他随机的东西时才有效(即未使用的空间是否全为零?)。哎呀,你可能只是删除所有尾随零 - 有一个边缘情况,有 合法 尾随零,但它比挂钩阅读器 API 更容易。你在这里说的是什么数量? 10? 1000? 1000000? @Marc,我最终删除了尾随零,它对我有用。这可能不是最理想的解决方案,但在我的情况下,这将很少执行,以至于它并不重要。我希望图书馆有一些很酷的方法来处理它,但我错了。

以上是关于ProtoBuf-Net 错误消息“源数据中的无效字段:0”的主要内容,如果未能解决你的问题,请参考以下文章

ProtoBuf-Net 错误消息 - “不支持嵌套或锯齿状列表和数组”

protobuf-net 子消息未正确读取

在反序列化过程中出现下一个异常:“源数据中的无效字段:0”。如何找出源代码中的原因/错误位置?

使用带有标志枚举的 ProtoBuf-Net 时出错

protobuf-net 中日期时间的 .proto 消息是啥

protobuf-net 中的动态 protobuf 消息