这也可能是由于尝试将多个 protobuf 消息写入单个流而导致的。解决方法是使用 SerializeWithLengthPrefix 和 DeserializeWithLengthPrefix。
为什么会这样:
protobuf 规范支持相当少量的连线类型(二进制存储格式)和数据类型(.NET 等数据类型)。此外,这不是 1:1,也不是 1:many 或 many:1 - 单个线型可用于多种数据类型,并且单个数据类型可以通过多种线型中的任何一种进行编码.因此,除非您已经知道 scema,否则您无法完全理解 protobuf 片段,因此您知道如何解释每个值。例如,当您读取 Int32 数据类型时,支持的线类型可能是“varint”、“fixed32”和“fixed64”,而在读取 String 数据类型时,唯一支持的线型是“字符串”。
如果data-type和wire-type之间没有兼容的映射,则无法读取数据,报此错误。
现在让我们看看为什么会出现这种情况:
[ProtoContract]
public class Data1
{
[ProtoMember(1, IsRequired=true)]
public int A { get; set; }
}
[ProtoContract]
public class Data2
{
[ProtoMember(1, IsRequired = true)]
public string B { get; set; }
}
class Program
{
static void Main(string[] args)
{
var d1 = new Data1 { A = 1};
var d2 = new Data2 { B = "Hello" };
var ms = new MemoryStream();
Serializer.Serialize(ms, d1);
Serializer.Serialize(ms, d2);
ms.Position = 0;
var d3 = Serializer.Deserialize<Data1>(ms); // This will fail
var d4 = Serializer.Deserialize<Data2>(ms);
Console.WriteLine("{0} {1}", d3, d4);
}
}
在上面,两条消息是直接写在彼此之后的。复杂之处在于:protobuf 是一种可附加的格式,附加的意思是“合并”。 protobuf 消息不知道自己的长度,因此读取消息的默认方式是:读取直到 EOF。但是,这里我们附加了两个 不同的 类型。如果我们读回来,它不知道我们什么时候读完第一条消息,所以它一直在读。当它从第二条消息中获取数据时,我们发现自己正在读取“字符串”线型,但我们仍在尝试填充 Data1 实例,其中成员 1 是 Int32。 “string”和Int32之间没有映射,所以会爆炸。
*WithLengthPrefix 方法允许序列化程序知道每条消息在哪里结束;因此,如果我们使用*WithLengthPrefix 序列化Data1 和Data2,然后使用*WithLengthPrefix 方法反序列化Data1 和Data2,那么它正确拆分传入的两个实例之间的数据,只将正确的值读入正确的对象。
此外,当像这样存储异构数据时,您可能还想为每个类另外分配(通过*WithLengthPrefix)不同的字段编号;这可以更好地了解正在反序列化的类型。 Serializer.NonGeneric 中还有一个方法可以用来反序列化数据而无需提前知道我们在反序列化什么:
// Data1 is "1", Data2 is "2"
Serializer.SerializeWithLengthPrefix(ms, d1, PrefixStyle.Base128, 1);
Serializer.SerializeWithLengthPrefix(ms, d2, PrefixStyle.Base128, 2);
ms.Position = 0;
var lookup = new Dictionary<int,Type> { {1, typeof(Data1)}, {2,typeof(Data2)}};
object obj;
while (Serializer.NonGeneric.TryDeserializeWithLengthPrefix(ms,
PrefixStyle.Base128, fieldNum => lookup[fieldNum], out obj))
{
Console.WriteLine(obj); // writes Data1 on the first iteration,
// and Data2 on the second iteration
}