Cela peut aussi être causée par une tentative d'écrire plus d'un protobuf message à un seul flux. La solution est d'utiliser SerializeWithLengthPrefix et DeserializeWithLengthPrefix.
Pourquoi ce qui se passe:
Le protobuf spécification prend en charge un nombre relativement faible de fil-types (le binaire formats de stockage) et les types de données (l' .NET, etc types de données). En outre, ce n'est pas 1:1, ni 1:plusieurs ou plusieurs:1 - un seul fil-type peut être utilisé pour de multiples types de données, et un seul type de données peuvent être codées en utilisant des multiples de fil-types. En conséquence, vous ne peut pas comprendre pleinement un protobuf fragment, sauf si vous connaissez déjà le scema, de sorte que vous savez comment interpréter chaque valeur. Lorsque vous êtes, disons, à la lecture d'un Int32
-type de données, le fil-types peuvent être "varint", "fixed32" et "fixed64", où-comme lors de la lecture d'un String
-type de données, la seule prise en charge de la fil-de type "string".
Si il n'est pas compatible carte entre le type de données et le fil-type, les données ne puissent être lues, et cette erreur est générée.
Maintenant, regardons pourquoi cela se produit dans le scénario ici:
[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);
}
}
Ci-dessus, deux messages sont écrits directement après les uns les autres. La complication est: protobuf est un appendable format, avec append sens "fusion". Un protobuf message ne connaît pas sa propre longueur, de sorte que le défaut de la lecture d'un message est: lire jusqu'à ce que les expressions du FOLKLORE. Cependant, ici, nous avons ajouté deux différents types. Si nous lisons ce retour, il ne sait pas quand nous avons fini de lire le premier message, il garde la lecture. Lorsqu'il reçoit les données de la deuxième message, nous nous trouvons à la lecture d'une "chaîne" de fil-type, mais nous sommes encore à essayer de remplir un Data1
de l'instance, pour laquelle les 1 Int32
. Il n'y a pas de correspondance entre la chaîne "string" et Int32
, alors qu'elle n'explose.
L' *WithLengthPrefix
méthodes permettent le sérialiseur de savoir où chaque message se termine; donc, si nous sérialiser un Data1
et Data2
à l'aide de l' *WithLengthPrefix
, alors désérialiser un Data1
et Data2
à l'aide de l' *WithLengthPrefix
méthodes, puis il correctement divise les données entrantes entre les deux cas, seule la lecture de la juste valeur dans le droit de l'objet.
En outre, lors de l'entreposage de données hétérogènes comme ça, vous pourriez voulez également affecter (via *WithLengthPrefix
) un autre domaine-le nombre de chaque catégorie; ce qui assure une plus grande visibilité de quel type est désérialisé. Il y a aussi une méthode en Serializer.NonGeneric
qui peut ensuite être utilisé pour désérialiser les données sans avoir besoin de connaître à l'avance ce que nous sommes désérialisation:
// 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
}