2009-04-06 8 views
2

J'essaie de sérialiser un très grand IEnumerable<MyObject> en utilisant un XmlSerializer sans conserver tous les objets en mémoire.en continu XML sérialisation en .net

Le IEnumerable<MyObject> est en fait paresseux ..

Je suis à la recherche d'une solution de streaming qui:

  1. Prenez un objet de la IEnumerable<MyObject> Sérialisez au flux sous-jacent en utilisant la sérialisation standard (Je ne veux pas handcraft le XML ici!)
  2. Jetez les dans les données de la mémoire et passer à la prochaine

J'essaie avec ce code:

using (var writer = new StreamWriter(filePath)) 
{ 
var xmlSerializer = new XmlSerializer(typeof(MyObject)); 
    foreach (var myObject in myObjectsIEnumerable) 
    { 
    xmlSerializer.Serialize(writer, myObject); 
    } 
} 

mais je reçois plusieurs en-têtes XML et je ne peux pas spécifier une balise racine <MyObjects> donc mon XML est invalide.

Une idée?

Merci

+0

Voir http://www.hanselman.com/blog/MixingXmlSerializersWithXElementsAndLINQToXML.aspx – bajafresh4life

Répondre

4

La classe XmlWriter est une API de streaming rapide pour la génération XML. Il est plutôt de bas niveau, MSDN a un article lors de l'instanciation d'un XmlWriter de validation en utilisant XmlWriter.Create().

Editer: lien résolu. Voici un exemple de code de l'article:

async Task TestWriter(Stream stream) 
{ 
    XmlWriterSettings settings = new XmlWriterSettings(); 
    settings.Async = true; 

    using (XmlWriter writer = XmlWriter.Create(stream, settings)) { 
     await writer.WriteStartElementAsync("pf", "root", "http://ns"); 
     await writer.WriteStartElementAsync(null, "sub", null); 
     await writer.WriteAttributeStringAsync(null, "att", null, "val"); 
     await writer.WriteStringAsync("text"); 
     await writer.WriteEndElementAsync(); 
     await writer.WriteCommentAsync("cValue"); 
     await writer.WriteCDataAsync("cdata value"); 
     await writer.WriteEndElementAsync(); 
     await writer.FlushAsync(); 
    } 
} 
+0

Le lien est cassé. Dommage que la réponse ne contienne pas la solution. –

+1

Merci pour l'avis @Rob, lien fixe et le code de l'article copié pour répondre. –

4

Voici ce que j'utilise:

using System; 
using System.Collections.Generic; 
using System.Xml; 
using System.Xml.Serialization; 
using System.Text; 
using System.IO; 

namespace Utils 
{ 
    public class XMLSerializer 
    { 
     public static Byte[] StringToUTF8ByteArray(String xmlString) 
     { 
      return new UTF8Encoding().GetBytes(xmlString); 
     } 

     public static String SerializeToXML<T>(T objectToSerialize) 
     { 
      StringBuilder sb = new StringBuilder(); 

      XmlWriterSettings settings = 
       new XmlWriterSettings {Encoding = Encoding.UTF8, Indent = true}; 

      using (XmlWriter xmlWriter = XmlWriter.Create(sb, settings)) 
      { 
       if (xmlWriter != null) 
       { 
        new XmlSerializer(typeof(T)).Serialize(xmlWriter, objectToSerialize); 
       } 
      } 

      return sb.ToString(); 
     } 

     public static void DeserializeFromXML<T>(string xmlString, out T deserializedObject) where T : class 
     { 
      XmlSerializer xs = new XmlSerializer(typeof (T)); 

      using (MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(xmlString))) 
      { 
       deserializedObject = xs.Deserialize(memoryStream) as T; 
      } 
     } 
    } 
} 

Ensuite il suffit d'appeler:

string xml = Utils.SerializeToXML(myObjectsIEnumerable); 

Je ne l'ai pas essayé, par exemple, un IEnumerable qui va chercher des objets un à un temps à distance, ou tout autre cas d'utilisation étrange, mais il fonctionne parfaitement pour List<T> et d'autres collections qui sont en mémoire.

EDIT: Sur la base de vos commentaires en réponse à cela, vous pouvez utiliser XmlDocument.LoadXml pour charger la chaîne XML résultant en un XmlDocument, sauf le premier à un fichier, et l'utiliser comme fichier XML maître. Pour chaque élément dans le IEnumerable, utilisez à nouveau LoadXml pour créer un nouveau XmlDocument en mémoire, saisissez les nœuds souhaités, ajoutez-les au document maître et enregistrez-le à nouveau, en vous débarrassant du nouveau. Une fois que vous avez terminé, il peut y avoir un moyen d'envelopper tous les nœuds dans votre balise racine. Vous pouvez également utiliser XSL et XslCompiledTransform pour écrire un autre fichier XML avec les objets correctement enveloppés dans la balise racine.

+1

Le problème ici est que je ne veux pas garder tous les objets ou l'ensemble XML doc/string en mémoire. Je veux vraiment sérialiser un objet à la fois et ajouter à un FileStream le XML. –

1

Vous pouvez le faire en implémentant l'interface IXmlSerializable sur la grande classe. L'implémentation de la méthode WriteXml peut écrire la balise de début, puis simplement faire une boucle sur le IEnumerable<MyObject> et sérialiser chaque MyObject au même XmlWriter, un à la fois.

Dans cette implémentation, il n'y aura pas aucune donnée en mémoire pour se débarrasser de (au-delà de ce que le garbage collector va collecter).