2009-04-06 5 views
2

Я пытаюсь сериализовать очень большой IEnumerable<MyObject> с использованием XmlSerializer, не сохраняя все объекты в памяти.потоковая сериализация XML в .net

IEnumerable<MyObject> на самом деле ленивый ..

Я ищу потоковое решение, которое:

  1. Возьмите объект из IEnumerable<MyObject> сериализовать его в основной поток, используя стандартные сериализации (Я не хочу HANDCRAFT в XML здесь!)
  2. Сливаем в памяти данных и перехода к следующему

Я пытаюсь с этим кодом:

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

, но я получаю несколько заголовков XML, и я не могу указать корневой тег <MyObjects> поэтому мой XML является недействительным.

Любая идея?

Благодаря

+0

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

ответ

4

Класс XmlWriter - это быстрый потоковый API для генерации XML. Он довольно низкоуровневый, MSDN имеет article при создании экземпляра XmlWriter, использующего XmlWriter.Create().

Редактировать: ссылка исправлена. Вот пример кода из статьи:

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

Ссылка не работает. Жаль, что ответ не содержал решения. –

+1

Спасибо за уведомление @Rob, ссылка исправлена ​​и код из статьи скопирован для ответа. –

4

Вот что я использую:

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

Тогда просто позвоните:

string xml = Utils.SerializeToXML(myObjectsIEnumerable); 

Я не пробовал его, например, IEnumerable, который выбирает объекты один на время удаленно или любые другие странные варианты использования, но он отлично работает для List<T> и других коллекций, которые находятся в памяти.

EDIT: на основе ваших комментариев в ответ на это, вы могли бы использовать XmlDocument.LoadXml загрузить полученную строку XML в XmlDocument, сохранить первый в файл и использовать его в качестве главного файла XML. Для каждого элемента в IEnumerable снова используйте LoadXml, чтобы создать новую в памяти XmlDocument, возьмите нужные узлы, добавьте их в главный документ и сохраните снова, избавившись от нового.

После того, как вы закончите, может быть способ обернуть все узлы в вашем корневом теге. Вы также можете использовать XSL и XslCompiledTransform для записи другого XML-файла с объектами, правильно упакованными в корневой тег.

+1

Проблема здесь состоит в том, что я не хочу, чтобы все объекты или весь XML doc/string в памяти. Я действительно хочу сериализовать один объект за раз и добавить в FileStream XML. –

1

Вы можете сделать это, реализовав интерфейс IXmlSerializable на большом классе. Реализация метода WriteXml может записать начальный тег, а затем просто перебрать по IEnumerable<MyObject> и сериализовать каждый MyObject на тот же XmlWriter по одному за раз.

В этой реализации не будет любых данных в памяти, чтобы избавиться (мимо того, что собирает сборщик мусора).

Смежные вопросы