2009-04-06 5 views
2

Ich versuche, eine sehr große IEnumerable<MyObject> mit einer XmlSerializer zu serialisieren, ohne alle Objekte im Speicher zu halten.Streaming-XML-Serialisierung in .net

Die IEnumerable<MyObject> ist eigentlich faul ..

ich für eine Streaming-Lösung bin auf der Suche, die wird:

  1. ein Objekt aus den IEnumerable<MyObject> Nehmen Sie sich zu dem zugrunde liegenden Stream serialisiert die Standard-Serialisierung mit (ich will nicht hier, um die XML-Handwerk!)
  2. die Daten im Speicher verwerfen und zum nächsten
bewegen

ich mit diesem Code versuchen:

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

aber ich bin immer mehrere XML-Header und ich kann nicht einen Root-Tag <MyObjects> so meine XML ist ungültig angeben.

Irgendeine Idee?

Dank

+0

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

Antwort

4

Die Klasse XmlWriter ist eine schnelle Streaming-API für die XML-Generierung. Es ist eher Low-Level, MSDN hat eine article auf Instanziieren eines validierenden XmlWriter mit XmlWriter.Create().

Edit: Link fixiert. Hier ist ein Beispielcode aus dem Artikel:

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

Die Verbindung ist unterbrochen. Schade, dass die Antwort die Lösung nicht enthielt. –

+1

Danke für die Benachrichtigung @Rob, Link behoben und Code aus dem Artikel kopiert zu beantworten. –

4

Hier ist, was ich benutze:

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; 
      } 
     } 
    } 
} 

Dann rufen Sie einfach:

string xml = Utils.SerializeToXML(myObjectsIEnumerable); 

Ich habe es nicht versucht, mit, zum Beispiel ein IEnumerable, die bei holt ein Objekt eine Zeit entfernt oder irgendwelche anderen seltsamen Anwendungsfälle, aber es funktioniert perfekt für List<T> und andere Sammlungen, die im Speicher sind.

EDIT: Basierend auf Ihre Kommentare als Reaktion auf diese, könnten Sie XmlDocument.LoadXml verwenden, um die resultierenden XML-String in ein XmlDocument, speichern die erste zu einer Datei zu laden und verwenden, die als XML-Datei-Master. Verwenden Sie für jeden Artikel in IEnumerable erneut LoadXml, um einen neuen speicherinternen XmlDocument zu erstellen, greifen Sie die gewünschten Knoten an, hängen Sie sie an das Masterdokument an und speichern Sie sie erneut, um den neuen zu entfernen.

Nachdem Sie fertig sind, gibt es möglicherweise eine Möglichkeit, alle Knoten in Ihrem Stamm-Tag zu umbrechen. Sie können auch XSL und XslCompiledTransform verwenden, um eine andere XML-Datei zu schreiben, in der die Objekte ordnungsgemäß im Root-Tag eingeschlossen sind.

+1

Das Problem hier ist, dass ich will nicht alle Objekte oder das gesamte XML halten doc/Zeichenfolge im Speicher. Ich möchte wirklich ein Objekt nach dem anderen serialisieren und die XML an einen FileStream anhängen. –

1

Sie können dies tun, indem Sie die Schnittstelle IXmlSerializable für die große Klasse implementieren. Die Implementierung der WriteXml Methode kann die Start-Tag schreiben, dann einfach über die IEnumerable<MyObject> Schleife und serialisieren jeweils MyObject auf die gleiche XmlWriter, eins nach dem anderen.

In dieser Implementierung wird nicht werden alle In-Memory-Daten loswerden (Vergangenheit, was der Garbage Collector sammeln wird).