使用 xmlReader 在 C# 中过滤特定元素值的大型 XML

Posted

技术标签:

【中文标题】使用 xmlReader 在 C# 中过滤特定元素值的大型 XML【英文标题】:Filter Large XML for specific element value in C# Using xmlReader 【发布时间】:2021-09-03 01:05:27 【问题描述】:

在这个线程中: Filter XML for specific element value in C#

我能够使用XDocument 过滤xml 文件以查找特定元素。但是,对于巨大的xml 文件,XDocument 似乎不是一个可行的解决方案,因为它会因System.OutOfMemoryException 消息而失败。挖掘了一下,看起来xmlReader 在处理大型xmls 时内存效率更高。

如何重写接受的答案,使用xmlReader,得到相同的结果?

【问题讨论】:

您的 xaml 是否具有静态结构?如果是这样,您可以尝试将其导入 c# 类 是的,反序列化它,然后砰!用你喜欢的方式过滤它。这不是一个选项吗? @Emanuele 是的,结构是静态的。您能否使用上述链接中的相同 xml 粘贴执行此操作的代码? 【参考方案1】:

请尝试以下解决方案。

它的可扩展性很强,可以毫无问题地处理多 GB 大小的 XML 文件。

XStreamingElement 正在使用一种扩展方法,该方法使用 XmlReader 流式传输由 <section>Section 1</section> 节点过滤的源 XML。

c#

void Main()

    const string inputXMLFile = @"e:\Temp\Sanosi.xml";
    const string outputXMLFile = @"e:\Temp\Sanosi_Streamed.xml";
    const string ROW = "Entry";
    const string FILTER = "Section 1";

    // Stream XML to file system
    System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
    timer.Start();

    // Shape output XML
    XStreamingElement newXML = new XStreamingElement("root",
       from element in StreamElements(inputXMLFile, ROW)
            .Where(x => x.Element("section").Value.Equals(FILTER))
       select new XElement(ROW, element.Elements("image")
          ));

    newXML.Save(outputXMLFile, SaveOptions.OmitDuplicateNamespaces);

    FileInfo fileBefore = new FileInfo(inputXMLFile);
    FileInfo fileAfter = new FileInfo(outputXMLFile);

    timer.Stop();

    Console.WriteLine("Streamed XML file '0', 1 bytes to file system as: '2', 3 bytes5Elapsed time: 4",
      fileBefore.FullName
      , fileBefore.Length
      , fileAfter.FullName
      , fileAfter.Length
      , timer.Elapsed
      , Environment.NewLine);


private static IEnumerable<XElement> StreamElements(string fileName, string elementName)

    using (var rdr = XmlReader.Create(fileName))
    
        rdr.MoveToContent();
        while (rdr.Read())
        
            if ((rdr.NodeType == XmlNodeType.Element) && (rdr.Name == elementName))
            
                var e = XElement.ReadFrom(rdr) as XElement;
                yield return e;
            
        
        rdr.Close();
    

【讨论】:

这不会解决内存不足的问题。 @jdweng,我经常使用这种方法来处理多 GB XML 文件。 Windows 10 操作系统任务管理器未显示任何内存使用高峰。 @jdweng,请在 LinkedIn 上与我联系。 您没有使用像 OP 这样的大文件。带有大文件的 XML linq 经常出现内存不足错误。解决的唯一方法是像我发布的代码一样使用 XmlReader。阅读问题。你的机器可能比 OP 有更多的内存。 @jdweng,仅供参考,使用该技术,我能够在具有 8 GB RAM 的机器上处理大小为 30+ GB 的 XML 文件。【参考方案2】:

我使用这样的代码:

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 != "Entry")
                
                    reader.ReadToFollowing("Entry");
                
                if (!reader.EOF)
                
                    XElement entry = (XElement)XElement.ReadFrom(reader);
                
            

        
    

【讨论】:

在最后一个 if 语句中获得“entry”xelement 后,如何搜索此元素以检查 Section = Section 1,以便获得我正在寻找的元素?类似 Entry.Descendants().Where(x => x.Element("section").Value.Equals("Section 1")).Elements("image"); 尝试以下操作:List images = entry.Descendants("image").ToList();

以上是关于使用 xmlReader 在 C# 中过滤特定元素值的大型 XML的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中使用 XmlReader 读取 Xml

XmlReader 创建空字符串 C#

C# 使用 XmlReader 但不使用 XmlDocument 获取额外的空白值

XmlReader - 自关闭元素不会触发 EndElement 事件?

XmlReader 跳过相邻元素

XmlReader - 如何在没有 System.OutOfMemoryException 的情况下读取元素中很长的字符串