使用 C# XmlSerializer 为大型对象集写入块以避免内存不足

Kai*_*ran 3 c# xml xmlserializer

我喜欢 XmlSerialize 的工作方式,如此简单和优雅,并且具有属性 =p 但是,在序列化为 xml 文件之前构建所有对象的集合时,我遇到了内存不足问题。

我正在从 SQL 数据库填充一个对象,并打算使用 XmlSerialize 将该对象写入 XML。它适用于小子集,但如果我尝试从数据库中获取所有对象,则会遇到内存不足异常。

XmlSerialize 是否有某种功能可以让我从数据库中抓取 100 个对象的批次,然后写入它们,抓取下一批 100 个对象并附加到 xml?

我希望我不必陷入 XmlDocument 或需要更多手动编码工作的东西......

dbc*_*dbc 5

XmlSerializer事实上,可以在序列化时传入和传出可枚举数据。它对实现IEnumerable<T>. 从文档

XmlSerializer 对实现 IEnumerable 或 ICollection 的类进行了特殊处理。实现 IEnumerable 的类必须实现一个接受单个参数的公共 Add 方法。Add 方法的参数必须与从 GetEnumerator 返回的值上的 Current 属性返回的类型或该类型的基数之一具有相同的类型。

序列化此类类时,XmlSerializer只需遍历可枚举,将每个当前值写入输出流。它不会首先将整个可枚举加载到列表中。因此,如果您有一些 Linq 查询T以块的形式从数据库中动态分页类型的结果(示例here),您可以使用以下包装器将它们全部序列化,而无需一次性加载它们:

// Proxy class for any enumerable with the requisite `Add` methods.
public class EnumerableProxy<T> : IEnumerable<T>
{
    [XmlIgnore]
    public IEnumerable<T> BaseEnumerable { get; set; }

    public void Add(T obj)
    {
        throw new NotImplementedException();
    }

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        if (BaseEnumerable == null)
            return Enumerable.Empty<T>().GetEnumerator();
        return BaseEnumerable.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

注意这个类只对序列化有用,对反序列化没有用。以下是如何使用它的示例:

public class RootObject<T>
{
    [XmlIgnore]
    public IEnumerable<T> Results { get; set; }

    [XmlArray("Results")]
    public EnumerableProxy<T> ResultsProxy { 
        get
        {
            return new EnumerableProxy<T> { BaseEnumerable = Results };
        }
        set
        {
            throw new NotImplementedException();
        }
    }
}

public class TestClass
{
    XmlWriter xmlWriter;
    TextWriter textWriter;

    public void Test()
    {
        try
        {
            var root = new RootObject<int>();
            root.Results = GetResults();

            using (textWriter = new StringWriter())
            {
                var settings = new XmlWriterSettings { Indent = true, IndentChars = "  " };
                using (xmlWriter = XmlWriter.Create(textWriter, settings))
                {
                    (new XmlSerializer(root.GetType())).Serialize(xmlWriter, root);
                }
                var xml = textWriter.ToString();
                Debug.WriteLine(xml);
            }
        }
        finally
        {
            xmlWriter = null;
            textWriter = null;
        }
    }

    IEnumerable<int> GetResults()
    {
        foreach (var i in Enumerable.Range(0, 1000))
        {
            if (i > 0 && (i % 500) == 0)
            {
                HalfwayPoint();
            }
            yield return i;
        }
    }

    private void HalfwayPoint()
    {
        if (xmlWriter != null)
        {
            xmlWriter.Flush();
            var xml = textWriter.ToString();
            Debug.WriteLine(xml);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果在 中设置中断HalfwayPoint(),您将看到一半的 XML 已经写出,同时仍在遍历可枚举项。(当然,我只是为了测试目的而写入字符串,而您可能会写入文件。)