简体   繁体   English

在C#Windows窗体中读取大型XML文件时出现OutofMemoryException

[英]OutofMemoryException while reading large XML file in C# windows forms

I have been storing the .png files into one of the XML element as below. 我一直将.png文件存储到以下XML元素之一中。 I have been converting the image file into base64 and then copying that string to XML. 我一直在将图像文件转换为base64,然后将该字符串复制到XML。 I was also able to read.load this XML file into my window form. 我还能够读取该XML文件并将其加载到我的窗口表单中。 The issue I am facing is as the XML file is growing with more nodes, XML file size has grown too large, right now it is 300 MB. 我面临的问题是随着XML文件越来越多,节点越来越多,XML文件的大小已经太大,现在是300 MB。 When the Windows forms is trying to read this large XML file I am getting OutOfMemoryExceptions . 当Windows表单尝试读取此大XML文件时,我收到OutOfMemoryExceptions Below is the snippet of my xml file. 以下是我的xml文件的代码段。

<TestResult>
    <ResultsID>49</ResultsID>
    <DateExecuted>2018-02-20T09:36:12.787</DateExecuted>
    <UserExecuted>xxx</UserExecuted>
    <CorrectedMean>1966.32245</CorrectedMean>
    <CorrectedVariance>19525.6632019949</CorrectedVariance>
    <TestPassed>true</TestPassed>
    <TestResultImage>Qk2.......</TestResultImage>
</TestResult>

I have been trying to load the XML into .net using the below code 我一直在尝试使用以下代码将XML加载到.net中

XDocument xmlResultsDoc = XDocument.Load("MeanData.xml");

and storing into my model class as below. 并存储到我的模型类中,如下所示。

List<MeanVarianceTestResultDataList = 
(xmlResultsDoc.Descendants("TestResult").Select(m => new 
MeanVarianceTestResultsData()
                    {
                        ResultsID = 
Convert.ToInt32(m.Element("ResultsID").Value),
                        DateExecuted = 
Convert.ToDateTime(m.Element("DateExecuted").Value),
                        UserExecuted = 
Convert.ToString(m.Element("UserExecuted").Value),
                        CorrectedMean = 
Convert.ToString(m.Element("CorrectedMean").Value),
                        CorrectedVariance = 
Convert.ToString(m.Element("CorrectedVariance").Value),
                        TestPassed = 
Convert.ToBoolean(m.Element("TestPassed").Value),
                        TestResultImage =  

Convert.FromBase64String(
Convert.ToString(m.Element("TestResultImage").Value))
                    })).ToList();

With huge xml files you must use XmlReader to prevent out-of-memory errors. 对于巨大的xml文件,您必须使用XmlReader来防止内存不足错误。 Try code below which uses a combination of xml linq and XmlReader 尝试下面的代码,该代码结合使用xml linq和XmlReader

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

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlReader reader = XmlReader.Create(FILENAME);

            while (!reader.EOF)
            {
                if (reader.Name != "TestResult")
                {
                    reader.ReadToFollowing("TestResult");
                }
                if (!reader.EOF)
                {
                    XElement testResult = (XElement)XElement.ReadFrom(reader);
                    string image = (string)testResult.Element("TestResultImage"); 
                }
            }
        }
    }
}

If your XML file is too large to load into memory at once, you can use XmlReader to stream through the file and load only small portions at a time. 如果XML文件太大而无法一次加载到内存中,则可以使用XmlReader来流传输该文件并一次仅加载一小部分。 Further, for the <TestResultImage> element which contains very large Base64-encoded binary data, you can use XmlReader.ReadElementContentAsBase64(Byte[], Int32, Int32) to incrementally read the data in chunks and copy them into some Stream . 此外,对于包含非常大的Base64编码二进制数据的<TestResultImage>元素,可以使用XmlReader.ReadElementContentAsBase64(Byte[], Int32, Int32)增量读取大块数据并将其复制到一些Stream

The following code shows how to accomplish this: 以下代码显示了如何完成此操作:

//https://stackoverflow.com/questions/49159697/deserializing-json-child-object-values-into-parent-object-using-jsonconvert-dese

public class MeanVarianceTestResultsData
{
    public int ResultsID { get; set; }
    public DateTime DateExecuted { get; set; }
    public string UserExecuted { get; set; }
    public string CorrectedMean { get; set; }
    public string CorrectedVariance { get; set; }
    public bool TestPassed { get; set; }

    public string TestResultImageFile { get; set; }
    public Stream TestResultImage { get; set; }
}

public static class MeanVarianceTestResultsDataExtensions
{
    public static List<MeanVarianceTestResultsData> ReadResultListFrom(XmlReader reader, Func<MeanVarianceTestResultsData, Stream> openStream, Func<Stream, Stream> closeStream)
    {
        return reader.ReadSubtrees("TestResult").Select(r => ReadResultFrom(r, openStream, closeStream)).ToList();
    }

    public static MeanVarianceTestResultsData ReadResultFrom(XmlReader reader, Func<MeanVarianceTestResultsData, Stream> openStream, Func<Stream, Stream> closeStream)
    {
        if (reader == null || openStream == null)
            throw new ArgumentNullException();
        reader.MoveToContent();
        var result = new MeanVarianceTestResultsData();
        var isEmpty = reader.IsEmptyElement;
        // Read the root
        reader.Read();
        if (isEmpty)
            return result;
        while (!reader.EOF)
        {
            if (reader.NodeType == XmlNodeType.EndElement)
            {
                reader.Read();
                break;
            }
            else if (reader.NodeType != XmlNodeType.Element)
                // Comment, text, CDATA, etc.
                reader.Skip();
            else if (reader.Name == "ResultsID")
                result.ResultsID = reader.ReadElementContentAsInt();
            else if (reader.Name == "DateExecuted")
                result.DateExecuted = reader.ReadElementContentAsDateTime();
            else if (reader.Name == "UserExecuted")
                result.UserExecuted = reader.ReadElementContentAsString();
            else if (reader.Name == "CorrectedMean")
                result.CorrectedMean = reader.ReadElementContentAsString();
            else if (reader.Name == "TestPassed")
                result.TestPassed = reader.ReadElementContentAsBoolean();
            else if (reader.Name == "TestResultImage")
                result.TestResultImage = reader.ReadElementContentAsStream(() => openStream(result), closeStream);
            else
                reader.Skip();
        }
        return result;
    }
}

public static class XmlReaderExtensions
{
    public static Stream ReadElementContentAsStream(this XmlReader reader, Func<Stream> openStream, Func<Stream, Stream> closeStream)
    {
        if (reader == null || openStream == null)
            throw new ArgumentNullException();
        Stream stream = null;
        try
        {
            stream = openStream();
            byte[] buffer = new byte[4096];
            int readBytes = 0;
            while ((readBytes = reader.ReadElementContentAsBase64(buffer, 0, buffer.Length)) > 0)
            {
                stream.Write(buffer, 0, readBytes);
            }
        }
        finally
        {
            if (closeStream != null && stream != null)
                stream = closeStream(stream);
        }
        return stream;
    }

    public static IEnumerable<XmlReader> ReadSubtrees(this XmlReader reader, string name)
    {
        while (reader.ReadToFollowing(name))
        {
            using (var subReader = reader.ReadSubtree())
                yield return subReader;
        }
    }
}

And then, you could use it as follows to read each TestResultImage images into a MemoryStream : 然后,您可以使用它如下将每个TestResultImage图像读入MemoryStream

List<MeanVarianceTestResultsData> results;
using (var reader = XmlReader.Create(fileName))
{
    results = MeanVarianceTestResultsDataExtensions.ReadResultListFrom(reader, m => new MemoryStream(), s => { s.Position = 0; return s; });
}

This will save substantial amounts of memory by completely skipping the intermediate Base64 string representation for the images - but it will still use quite a lot of memory for each MemoryStream . 通过完全跳过图像的中间Base64字符串表示,这将节省大量内存-但每个MemoryStream仍将使用大量内存。 Alternatively, you could stream the images into some temporary files for later use, eg by doing the following: 或者,您可以将图像流式传输到一些临时文件中以供以后使用,例如,通过执行以下操作:

List<MeanVarianceTestResultsData> results;
using (var reader = XmlReader.Create(fileName))
{
    results = MeanVarianceTestResultsDataExtensions.ReadResultListFrom(
        reader,
        m => { m.TestResultImageFile = Path.GetTempFileName(); return File.Open(m.TestResultImageFile, FileMode.Create); },
        s => { s.Dispose(); return null; });
}

In this case each stream is disposed after the image is written and the file name is stored in the MeanVarianceTestResultsData . 在这种情况下,在写入图像并将文件名存储在MeanVarianceTestResultsData之后,将处理每个流。 (Of course, you could leave the streams open if you plan to immediately process them after deserialization.) (当然,如果您打算在反序列化之后立即处理这些流,则可以将其保持打开状态。)

Sample fiddle . 样品提琴

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用C#读取大型文本文件时出现System.OutofMemoryException - System.OutofMemoryException while reading a large text file using C# 读取大文件-System.OutOfMemoryException:引发了类型为&#39;System.OutOfMemoryException&#39;的异常。 在C#中 - Reading large file - System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown. in C# 读取文件时出现OutOfMemoryException - OutOfMemoryException while reading a file Windows Forms应用程序用于读取和写入非常大的XML文件,但它是悬挂的 - Windows Forms application for reading and writing a very large XML file, but it is is hanging C#错误:OutOfMemoryException-读取大文本文件并从字典中替换 - C# Error: OutOfMemoryException - Reading a large text file and replacing from dictionary 在C#窗体中读取xml(.rtdl)文件时路径中的非法字符 - illegal characters in path while reading xml(.rtdl)files in C# windows forms ReadOuterXml 正在抛出 OutOfMemoryException 读取大(1 GB)XML 文件的一部分 - ReadOuterXml is throwing OutOfMemoryException reading part of large (1 GB) XML file 反序列化XML文件时C#中的OutOfMemoryException - OutOfMemoryException in c# when deserializing XML file 大数组C#OutOfMemoryException - Large array C# OutOfMemoryException ASP.NET C#OutofMemoryException大文件上载 - ASP.NET C# OutofMemoryException On Large File Upload
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM