【问题标题】:Loading large XML on DataSet (OutOfMemory Exception)在 DataSet 中加载大型 XML (OutOfMemoryException)
【发布时间】:2016-07-17 12:17:21
【问题描述】:

我正在尝试通过 URl 读取 3GB XML 文件并将所有作业存储在数据集中。 XML 看起来像这样:

    <?xml version="1.0"?>
    <feed total="1621473">
      <job>
        <title><![CDATA[Certified Medical Assistant]]></title>
        <date>2016-03-25 14:19:38</date>
        <referencenumber>2089677765</referencenumber>
        <url><![CDATA[http://www.jobs2careers.com/click.php?id=2089677765.1347]]></url>
        <company><![CDATA[Broadway Medical Clinic]]></company>
        <city>Portland</city>
        <state>OR</state>
        <zip>97213</zip>
     </job>
     <job>
        <title><![CDATA[Certified Medical Assistant]]></title>
        <date>2016-03-25 14:19:38</date>
        <referencenumber>2089677765</referencenumber>
        <url><![CDATA[http://www.jobs2careers.com/click.php?id=2089677765.1347]]></url>
        <company><![CDATA[Broadway Medical Clinic]]></company>
        <city>Portland</city>
        <state>OR</state>
        <zip>97213</zip>
     </job>
    </feed>

这是我的代码

XmlDocument doc = new XmlDocument();
            doc.Load(url);
            DataSet ds = new DataSet();
            XmlNodeReader xmlReader = new XmlNodeReader(doc);

            while (xmlReader.ReadToFollowing("job"))
            {
                ds.ReadXml(xmlReader);
            }

但是我得到了内存溢出异常。在谷歌上浏览 发现了这个:

DataSet ds = new DataSet();
        FileStream filestream = File.OpenRead(url);
        BufferedStream buffered = new BufferedStream(filestream);
        ds.ReadXml(buffered);

还是一样的例外。我还阅读了有关 XmlTextReader 的信息,但我不知道如何在我的情况下使用它。 我知道为什么会出现异常,但我不知道如何克服。谢谢

【问题讨论】:

  • 异常详情是什么?我怀疑它可能是抛出“OutOfMemoryException”的 XmlDocument。原因是我整理了一些代码来生成一个大的 XML 文件,在我生成足够的数据之前,我构建的 XmlDocument 对象正在抛出。可能与节点的内部集合有关({System.Collections.ListDictionaryInternal.NodeKeyValueCollection})。
  • 你想要什么输出?我不明白“让所有工作都痛苦”。
  • @MichaelKay:我的错,已编辑。我想将所有作业存储在数据集中,以便稍后我可以将所有作业存储在数据库表中。
  • @Stringfellow 在 XMLDocument 实例上调用 load 方法会尝试一次加载整个文件。该文件为 3 GB,因此发生异常。

标签: c# xml visual-studio-2012 dataset


【解决方案1】:

doc.Load() 将读取整个文件并给出错误。 XmlNodeReader 不会真正为您做任何事情。试试这个

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Data;

namespace ConsoleApplication1
{
    class Program
    {
        const string url = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            int count = 0;
            DataSet ds = new DataSet();
            XmlReader xmlReader = XmlReader.Create(url);
            xmlReader.MoveToContent();
            try
            {
                while (!xmlReader.EOF)
                {
                    count++;
                    xmlReader.ReadToFollowing("job");
                    if (!xmlReader.EOF)
                    {
                        ds.ReadXml(xmlReader);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Count : {0}", count);
                Console.ReadLine();
            }
            
        }
    }

}

【讨论】:

  • 我仍然在 ds.ReadXml() 上得到 System.OutOfMemoryException
  • 我更新了代码以删除一些拼写错误。不确定它是否会解决问题。你知道在异常之前读取了多少行作业元素吗?
  • 感谢您的宝贵时间。没有仍然是同样的例外。我试图调试它,但它不会让我知道读取了多少行。我想必须有一种方法可以将 xml 文件分成块然后逐个读取它们,或者通过缓冲区读取文件,这样就不会立即加载整个文件。我只是不知道如何实现它。
  • 添加异常处理程序以获取计数。您可能只是使用了比计算机更多的内存。
【解决方案2】:

与其尝试将整个文件加载到 DataSet 或其他容器中,不如加载批次并将每个批次写入数据库,以便每次都可以清除持有该批次的任何内容?

如何:执行大型 XML 文档的流式转换 https://msdn.microsoft.com/en-us/library/bb387013.aspx

        List<XElement> jobs = new List<XElement>();
        using (XmlReader reader = XmlReader.Create(filePath))
        {
            XElement job;
            reader.MoveToContent();
            while (reader.Read())
            {
                if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "job"))
                {
                    job = XElement.ReadFrom(reader) as XElement;
                    jobs.Add(job);

                    if (jobs.Count >= 1000)
                    {
                        // TODO: write batch to database
                        jobs.Clear();
                    }
                }
            }

            if (jobs.Count > 0)
            {
                // TODO: write remainder to database
                jobs.Clear();
            }

        }

使用数据集的替代方法。

        DataSet ds = new DataSet();
        using (XmlReader reader = XmlReader.Create(filePath))
        {
            reader.MoveToContent();
            while (reader.Read())
            {
                if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "job"))
                {
                    ds.ReadXml(reader);

                    DataTable dt = ds.Tables["job"];
                    if (dt.Rows.Count >= 1000)
                    {
                        // TODO: write batch to database
                        dt.Rows.Clear();
                    }
                }
            }

            if (ds.Tables["job"].Rows.Count > 0)
            {
                // TODO: write remainder to database
                ds.Tables["job"].Rows.Clear();
            }
        }

【讨论】:

  • 感谢您的宝贵时间。并使用此代码如何填充我的数据集?
  • 我添加了一个替代方案。这就是你加载数据集的意思吗?我不知道您是否可以将整个 3 GB 文件加载到 DataSet 中而不会遇到内存问题。此外,通过批处理,您可以启用“恢复”方案,以防处理中途失败。
  • 数据集由 2 行填充,然后第一个 if 语句变为 false,知道为什么吗?仍在努力。你的解决方案听起来很坚定,我会告诉你的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多