【问题标题】:Reading XML File ( File size > 500 MB)读取 XML 文件(文件大小 > 500 MB)
【发布时间】:2018-08-08 05:19:10
【问题描述】:

我正在尝试解析大型 XML 文件(大小接近 600MB)并使用

这需要更长的时间,最后,整个过程中止。该过程以异常结束。

消息:“线程已中止”

方法:

private string ReadXml(XmlTextReader reader, string fileName)
{
    string finalXML = "";
    string s1 = "";
    try
    {
        while (reader.Read())
        {
            switch (reader.NodeType)
            {
                case XmlNodeType.Element: // The node is an element.
                    s1 += "<" + reader.Name + ">";
                    break;
                case XmlNodeType.Text: //Display the text in each element.
                    s1 += reader.Value;
                    break;
                case XmlNodeType.EndElement: //Display the end of the element.
                    s1 += "</" + reader.Name + ">";
                    break;
            }
            finalXML = s1;
        }
    }
    catch (Exception ex)
    {
       Logger.Logger.LogMessage(ex, "File Processing error: " + fileName);
    }
    reader.Close();
    reader.Dispose();

    return finalXML;
}

然后读取和脱盐:

string finalXML = string.Empty;
XmlTextReader reader = new XmlTextReader(unzipfile);
finalXML = await ReadXml(reader, fileName);

var xmlremovenamespae = Helper.RemoveAllNamespaces(finalXML);
XmlParseObjectNew.BizData myxml = new XmlParseObjectNew.BizData();

using (StringReader sr = new StringReader(xmlremovenamespae))
 {
       XmlSerializer serializer = new XmlSerializer(typeof(XmlParseObjectNew.BizData));
       myxml = (XmlParseObjectNew.BizData)serializer.Deserialize(sr);
 }

有没有更好的方法来读取和解析大型 xml 文件?需要一个建议。

【问题讨论】:

  • 你到底想在这里实现什么?你期望最终的字符串有多大? (提示:如果它很大,这可能不是一个好主意。使用这样的字符串连接当然不是一个好主意。)
  • Jon 打败了我,但是,是的,你想在这里做什么?如果您使用字符串连接,最好使用StringBuilder。但是,根据您要实现的目标,可能会有更有效的方法,例如,使用 XmlDocument
  • 感谢@DiskJunky 之前尝试使用 StringBuilder 但出现“内存不足”异常。
  • @Anadi 很公平,但是你能描述一个小的 XML sn-p 输入和你想要的输出吗?
  • @DiskJunky 但是XmlDocument等DOM方法会很快占用大量内存。对于较大的文件,我建议使用 XmlReader

标签: c# .net xml


【解决方案1】:

我试试这个,效果很好。

fileName = "你的文件路径";

试试这个代码,它可以在几秒钟内解析超过 500MB 的 XML 文件。

using (TextReader textReader = new StreamReader(fileName))
  {
    using (XmlTextReader reader = new XmlTextReader(textReader))
      {                                   
       reader.Namespaces = false;
 XmlSerializer serializer = new XmlSerializer(typeof("YourXmlClassType"));
          parseData = ("YourXmlClassType")serializer.Deserialize(reader);
      }
  }

【讨论】:

    【解决方案2】:

    问题是,正如 Jon Skeet 和 DiskJunky 所提到的,您的数据集太大而无法加载到内存中,并且您的代码没有针对处理这个问题进行优化。因此,为什么各种类会抛出“内存不足异常”。

    首先,字符串连接。由于字符串的工作方式,对多个字符串使用简单的串联 (a + b) 通常不是一个好主意。我建议在网上查找如何有效处理字符串连接(例如,Jon Skeet 的Concatenating Strings Efficiently)。

    然而,这是对您的代码的优化,主要问题是您尝试加载到内存中的 XML 文件的绝对大小。要处理大型数据集,最好能“流式传输”数据,处理数据块而不是整个文件。


    由于您没有展示您的 XML 示例,因此我冒昧地制作了一个简单的示例来说明我的意思。

    假设您有以下 XML:

    <root>
       <specialelement>
          <value1>somevalue</value1>
          <value2>somevalue</value2>
       </specialelement>
       <specialelement>
          <value1>someothervalue</value1>
          <value2>someothervalue</value2>
       </specialelement>
       ... 
    </root>
    

    你想将这个 XML 中的 specialelement 解析成一个对象,类定义如下:

    [XmlRoot("specialelement")]
    public class ExampleClass
    {
        [XmlElement(ElementName = "value1")]
        public string Value1 { get; set; }    
        [XmlElement(ElementName = "value2")]
        public string Value2 { get; set; }
    }
    

    我假设我们可以单独处理每个SpecialElement,并为此定义一个处理程序,如下所示:

    public void HandleElement(ExampleClass item)
    {
        // Process stuff
    }
    

    现在我们可以使用XmlTextReader 单独读取XML 中的每个元素,当我们到达specialelement 时,我们会跟踪XML 元素中包含的数据。当我们到达specialelement 的末尾时,我们将其反序列化为一个对象并将其发送给我们的处理程序进行处理。例如:

    using (var reader = new XmlTextReader( /* your inputstream */ ))
    {
        // Buffer for the element contents
        StringBuilder sb = new StringBuilder(1000);
    
        // Read till next node
        while (reader.Read())
        {
            switch (reader.NodeType)
            {
                case XmlNodeType.Element: 
                    // Clear the stringbuilder when we start with our element 
                    if (string.Equals(reader.Name, "specialelement"))
                    {
                        sb.Clear();
                    }
    
                    // Append current element without namespace
                    sb.Append("<").Append(reader.Name).Append(">");
                    break;
    
                case XmlNodeType.Text: //Display the text in each element.
                    sb.Append(reader.Value);
                    break;
    
                case XmlNodeType.EndElement: 
    
                    // Append the closure element
                    sb.Append("</").Append(reader.Name).Append(">");
    
                    // Check if we have finished reading our element
                    if (string.Equals(reader.Name, "specialelement"))
                    {
                        // The stringbuilder now contains the entire 'SpecialElement' part
                        using (TextReader textReader = new StringReader(sb.ToString()))
                        {
                            // Deserialize
                            var deserializedElement = (ExampleClass)serializer.Deserialize(textReader);
                            // Send to handler
                            HandleElement(deserializedElement);
                        }
                    }
    
                    break;
            }
        }
    }
    

    当我们开始处理来自流的数据时,我们不必将整个文件加载到内存中。保持程序的低内存使用率(防止内存不足异常)。

    结帐this fiddle 以查看实际情况。

    请注意,这是一个简单的示例,仍有很多地方可以进一步改进和优化此代码。

    【讨论】:

      猜你喜欢
      • 2019-02-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-02
      • 1970-01-01
      • 1970-01-01
      • 2021-05-14
      • 2019-08-17
      • 2015-11-11
      相关资源
      最近更新 更多