потоковая XML-сериализация в .net - PullRequest
3 голосов
/ 06 апреля 2009

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

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

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

  1. Возьмите предмет из IEnumerable<MyObject> Сериализуйте его в базовый поток, используя стандартную сериализацию ( Я не хочу здесь вручную создавать 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 недопустим.

Есть идеи?

Спасибо

Ответы [ 3 ]

4 голосов
/ 13 мая 2009

Класс XmlWriter является быстрым потоковым API для генерации XML. Это довольно низкий уровень, в MSDN есть статья о создании проверяющего 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();
    }
}
4 голосов
/ 06 апреля 2009

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

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 голос
/ 07 апреля 2009

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

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

...