【问题标题】:streaming XML serialization in .net.net 中的流式 XML 序列化
【发布时间】:2009-04-06 13:51:41
【问题描述】:

我正在尝试使用XmlSerializer 序列化一个非常大的IEnumerable<MyObject>,而不将所有对象保留在内存中。

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 无效。

有什么想法吗?

谢谢

【问题讨论】:

标签: c# .net xml serialization streaming


【解决方案1】:

XmlWriter 类是用于 XML 生成的快速流 API。这是相当低级的,MSDN 有一个 article 使用 XmlWriter.Create() 实例化验证 XmlWriter。

编辑:链接已修复。以下是文章中的示例代码:

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

【讨论】:

  • 链接已损坏。可惜答案没有包含解决方案。
  • 感谢@Rob 的通知,链接已修复,文章中的代码已复制到答案。
【解决方案2】:

这是我使用的:

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&lt;T&gt; 和内存中的其他集合。

编辑:根据您的 cmets 对此的响应,您可以使用 XmlDocument.LoadXml 将生成的 XML 字符串加载到 XmlDocument 中,将第一个字符串保存到文件中,然后使用它作为您的主 XML 文件。对于IEnumerable 中的每个项目,再次使用LoadXml 创建一个新的内存XmlDocument,获取所需的节点,将它们附加到主文档,然后再次保存,摆脱新的。

完成后,可能有一种方法可以将所有节点包装在根标记中。您还可以使用 XSL 和 XslCompiledTransform 编写另一个 XML 文件,其中对象正确包装在根标记中。

【讨论】:

  • 这里的问题是我不想将所有对象或整个 XML 文档/字符串保留在内存中。我真的很想一次序列化一个对象并将 XML 附加到 FileStream。
【解决方案3】:

您可以通过在大型类上实现IXmlSerializable 接口来做到这一点。 WriteXml 方法的实现可以编写开始标签,然后简单地循环 IEnumerable&lt;MyObject&gt; 并将每个 MyObject 序列化为相同的 XmlWriter,一次一个。

在这个实现中,不会有任何内存中的数据需要删除(除了垃圾收集器将收集的数据)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多