2009-06-11 4 views
1

Est-il possible de désérialiser uniquement un nombre limité d'éléments d'un tableau sérialisé?Désérialiser uniquement les premiers éléments x d'un tableau

Contexte:

j'ai un ruisseau qui tient un tableau sérialisé de type T. Le tableau peut avoir des millions d'articles, mais je veux créer un aperçu du contenu et récupérer uniquement les, disons, d'abord cent articles. Ma première idée était de créer un wrapper autour du flux d'entrée qui limite le nombre d'octets, mais il n'y a pas de traduction directe du nombre d'éléments du tableau à la taille du flux.

Répondre

1

Non, cela ne peut pas être effectué avec la sérialisation .NET standard. Vous devrez inventer votre propre format de stockage. Par exemple, inclure un en-tête avec des décalages de blocs de données:

---------------- 
<magic-value> 
<chunks-count> 
<chunk-size> 
<chunk-1-offset> 
<chunk-2-offset> --+ 
...     | 
---------------- | 
...     | 
<chunk-1>   | 
...     | 
---------------- | 
...    <-+ 
<chunk-2> 
... 
----------------- 
... 

Ainsi, afin de données (aperçu de toute position arbitraire), vous devez charger au plus ceil(required-item-count/chunk-size). Cela entraînera des frais généraux, mais c'est beaucoup mieux que de charger le fichier entier.

+0

Donc, je dois enregistrer le tableau en morceaux et accepter qu'il charge un peu plus? – Rauhotz

0

Pourriez-vous modifier votre source de données afin qu'elle contienne un aperçu des données dans un autre tableau que vous pouvez désérialiser séparément?

1

Qu'est-ce que le sérialiseur? Avec BinaryFormatter, ce serait très, très difficile.

Avec xml, vous pourriez peut-être pré-traiter le XML, mais c'est très compliqué.

D'autres sérialiseurs existent cependant - par exemple, avec protobuf-net il y a peu de différence entre un tableau/liste d'éléments, et une séquence d'éléments individuels - donc il serait assez facile de choisir une séquence finie d'éléments sans traiter le tableau entier.


Exemple complet protobuf-net:

[ProtoContract] 
class Test { 
    [ProtoMember(1)] 
    public int Foo { get; set; } 
    [ProtoMember(2)] 
    public string Bar { get; set; } 

    static void Main() { 
     Test[] data = new Test[1000]; 
     for (int i = 0; i < 1000; i++) { 
      data[i] = new Test { Foo = i, Bar = ":" + i.ToString() }; 
     } 
     MemoryStream ms = new MemoryStream(); 
     Serializer.Serialize(ms, data); 
     Console.WriteLine("Pos after writing: " + ms.Position); // 10760 
     Console.WriteLine("Length: " + ms.Length); // 10760 
     ms.Position = 0; 
     foreach (Test foo in Serializer.DeserializeItems<Test>(ms, 
       PrefixStyle.Base128, Serializer.ListItemTag).Take(100)) { 
      Console.WriteLine(foo.Foo + "\t" + foo.Bar); 
     } 
     Console.WriteLine("Pos after reading: " + ms.Position); // 902 

    } 
} 

Notez que DeserializeItems<T> est une API paresseux/lecture en transit, il ne consomme que les données du flux que vous itérer dessus - d'où le LINQ Take(100) nous évite lire tout le flux.

+0

Est-ce qu'un peu de hackery implémentant l'interface ISerializable ne l'obtiendrait pas ce qu'il veut? – Noldorin

+0

@Noldorin - pas autant que je sache ... vous n'obtenez pas d'intercepter la désérialisation de la matrice, quelle que soit la façon dont vous gérez chaque élément individuel (via ISerializable) –

Questions connexes