Comment sérialiser/désérialiser une grande liste d'articles avec protobuf-net

j'ai une liste d'environ 500 millions d'articles. Je suis capable de sérialiser ceci dans un fichier avec le fichier protobuf-net si je sérialise des articles individuels, pas une liste -- Je ne peux pas recueillir les articles dans la liste de prix et puis sérialiser parce que je cours hors de la mémoire. Donc, je dois sérialiser un enregistrement à la fois:

using (var input = File.OpenText("..."))
using (var output = new FileStream("...", FileMode.Create, FileAccess.Write))
{
    string line = "";
    while ((line = input.ReadLine()) != null)
    {
        Price price = new Price();
        (code that parses input into a Price record)

        Serializer.Serialize(output, price);
    }
}

ma question porte sur la partie de la désérialisation. Il semble que la méthode Deserialize ne déplace pas la Position du flux vers l'enregistrement suivant. Je essayé:

using (var input = new FileStream("...", FileMode.Open, FileAccess.Read))
{
    Price price = null;
    while ((price = Serializer.Deserialize<Price>(input)) != null)
    {
    }
}

je vois un enregistrement de prix réel, et puis le reste sont des enregistrements vides -- je récupère l'objet de prix mais tous les champs sont initialisés aux valeurs par défaut.

comment désérialiser correctement un flux qui contient une liste d'objets qui ne sont pas sérialisés comme une liste?

8
demandé sur user1044169 2011-11-25 21:18:34

3 réponses

bonnes nouvelles! L'API protobuf-net est configurée pour exactement ce scénario. Vous devriez voir un SerializeItems et DeserializeItems paire de méthodes qui fonctionnent avec IEnumerable<T>, permettant l'entrée et la sortie en continu. La façon la plus simple de l'alimenter en un énumérat est de passer par un "bloc itérateur" sur les données source.

Si, pour quelque raison que ce soit, qui n'est pas commode, c'est 100% identiques à l'aide de SerializeWithLengthPrefix et DeserializeWithLengthPrefix par élément de base, en précisant (comme les paramètres) champ: 1 et un préfixe de style de base-128. Vous pouvez même utiliser SerializeWithLengthPrefix pour l'écriture, et DeserializeItems pour la lecture (aussi longtemps que vous utilisez le champ 1 et la base-128).

Re l'exemple - id ont à voir que dans un entièrement reproductibles scénario de commentaire; en fait, ce que je voudrais attendre il y a que vous obtenez seulement un seul objet back out, contenant les valeurs combinées de chaque objet - parce que sans le préfixe length -, le protobuf spec suppose que vous êtes juste concaténer des valeurs à un objet simple. Les deux approches mentionnées ci-dessus évitent cette question.

4
répondu Marc Gravell 2011-11-25 18:21:25

Peut-être que je suis trop en retard sur ce... mais juste pour ajouter à ce que Marc a déjà dit.

comme vous utilisez Serializer.Serialize(output, price); protobuf traitez les messages consécutifs comme faisant partie d'un (même)seul objet. Ainsi, lorsque vous utilisez Deserialize en utilisant

while ((price = Serializer.Deserialize<Price>(input)) != null)

vous récupérerez tous les enregistrements. Par conséquent, vous ne verrez que le dernier record de prix.

Pour faire ce que vous voulez faire, modifiez le code de sérialisation:

Serializer.SerializeWithLengthPrefix(output, price, PrefixStyle.Base128, 1);

et

while ((price = Serializer.DeserializeWithLengthPrefix<Price>(input, PrefixStyle.Base128, 1)) != null)
2
répondu Vic 2013-05-25 01:47:39

l'API semble avoir changé depuis la réponse de Marc.

Il semble qu'il n'y ait plus de méthode SerializeItems.

Voici d'autres informations à jour qui devraient aider:

ProtoBuf.Serializer.Serialize(stream, items);

peut prendre un IEnumerable comme vu ci-dessus et il fait le travail quand il s'agit de la sérialisation.

Cependant il y a un DeserializeItems(...) la méthode et le diable est dans les détails :)

Si vous serialisez IEnumerable comme ci-dessus, alors vous devez appeler DeserializeItems passing PrefixStyle.Base128 et 1 comme fieldNumber cause apprently ceux qui sont les valeurs par défaut.

Voici un exemple:

ProtoBuf.Serializer.DeserializeItems<T>(stream, ProtoBuf.PrefixStyle.Base128, 1));

comme le font remarquer Marc et Vic, vous pouvez sérialiser/deserialiser par article comme ceci (en utilisant des valeurs personnalisées pour PrefixStyle et fieldNumber):

ProtoBuf.Serializer.SerializeWithLengthPrefix(stream, item, ProtoBuf.PrefixStyle.Base128, fieldNumber: 1);

et

T item;
while ((item = ProtoBuf.Serializer.DeserializeWithLengthPrefix<T>(stream, ProtoBuf.PrefixStyle.Base128, fieldNumber: 1)) != null)
{
    // do stuff here
}
0
répondu Piotr Owsiak 2017-02-20 14:01:32