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
这完全取决于上下文;在您展示的场景中(大多数Stream
s 等),长度很容易获得,整个事情只是通过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 错误消息 - “不支持嵌套或锯齿状列表和数组”
在反序列化过程中出现下一个异常:“源数据中的无效字段:0”。如何找出源代码中的原因/错误位置?