使用ReadOnlyMemory反序列化protobuf-net失败,报错「Invalid wire type (VarInt)」原因?
问题
使用流进行protobuf反序列化时可以成功,但改用ReadOnlySpan<byte>实例替代流时,会抛出ProtoException异常,提示「Invalid wire type (VarInt)」。使用的protobuf-net版本为3.1.26。
复现代码:
using (var stream = new MemoryStream()) { var test = 1; Serializer.Serialize(stream, test); // 保存字节用于后续对比 var bytes = stream.ToArray(); // 反序列化成功 stream.Seek(0, SeekOrigin.Begin); var item = Serializer.Deserialize<int>(stream); // 反序列化成功 stream.Seek(0, SeekOrigin.Begin); var item2 = Serializer.Deserialize(typeof(int), stream); stream.Seek(0, SeekOrigin.Begin); var rom = new ReadOnlySpan<byte>(stream.ToArray()); // 完整性校验:newBytes与bytes一致,实际确实一致 var newBytes = rom.ToArray(); // 此处抛出ProtoException异常:"Invalid wire-type (Varint)..." var item3 = Serializer.Deserialize(rom, typeof(int)); }
原因与解决方法
这个问题的核心是protobuf-net中针对流和针对ReadOnlySpan<byte>的序列化/反序列化逻辑存在格式差异:
- 用
Serializer.Serialize(stream, test)序列化单个int时,protobuf-net默认会写入带字段标识的完整protobuf结构(包含字段标签和wire type信息)。 - 但调用
Serializer.Deserialize(rom, typeof(int))时,该重载默认期望的是无字段标识的原始值格式(直接存储的varint数据,不带字段标签)。
两种格式不匹配,导致解析时识别到错误的wire type,抛出异常。
解决方法有两种:
- 序列化时统一使用无字段标识格式:
如果后续要通过ReadOnlySpan<byte>反序列化,序列化时调用针对Span<byte>的重载,确保格式一致:var buffer = new byte[Serializer.GetProtoMaxLength(test)]; var length = Serializer.Serialize(buffer, test); var rom = new ReadOnlySpan<byte>(buffer, 0, length); var item3 = Serializer.Deserialize(rom, typeof(int)); - 反序列化时明确解析带字段标识的结构:
使用ProtoReader手动处理,先读取字段头信息再解析值:using var reader = new ProtoReader(rom, null, new ProtoReader.State()); reader.ReadFieldHeader(); // 读取字段标签和wire type var item3 = reader.ReadInt32();
内容的提问来源于stack exchange,提问作者Elroy Flynn
相关产品推荐
相关产品推荐

