【问题标题】:re-writing large XML Files - excluding certain node重写大型 XML 文件 - 不包括某些节点
【发布时间】:2016-09-11 13:35:50
【问题描述】:

我想重写一个没有某些节点的大型 xml。 我正在尝试使用 system.xml.xmlreader 逐行读取 XML 文件(100 MB,无法将其全部读取到内存中) - 努力寻找一种读取其中部分内容的方法,将它们写到单独的xDocument,然后将该 xDocument 保存到磁盘。

我一直在想的是这样的:

      using (XmlReader reader = XmlReader.Create(_xml_path))
        {
            using (XmlWriter writer = XmlWriter.Create(@"filteredxml.xml"))
            {
                reader.MoveToContent();

                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        if (reader.Name != "EL_TO_BE_REMOVED")
                        {
                           //writer.WriteNode(reader.ReadOuterXml());

                        }
                    }
                }
            }
        }

但是 reader.ReadOuterXml() 只是转到第一个元素并将其所有后代写入文件,而不让我过滤我希望忽略的元素。

【问题讨论】:

标签: c# .net xml linq-to-xml


【解决方案1】:

在大文件和内存限制的情况下,您应该使用 SAX 而不是 DOM 进行解析:XMLReader 确实是 C# 等价物。

这可能是一种基本方法,其中一个 XMLReader 用于输入,一个 XMLWriter 用于输出,一个计数器用于删除名为 RemoveMe(及其所有内容)的节点。

注意内部循环克隆每个相关元素的属性。

        using (XmlReader reader = XmlReader.Create(OriginalXml))
        {
            XmlWriterSettings ws = new XmlWriterSettings();
            ws.Indent = true;
            using (XmlWriter writer = XmlWriter.Create(FilteredXml, ws))
            {
                int skip = 0;
                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                        case XmlNodeType.Element:
                            skip += reader.Name.Equals(RemoveMe) ? 1 : 0;
                            if (skip == 0)
                            {
                                writer.WriteStartElement(reader.Name);
                                while (reader.MoveToNextAttribute())
                                    writer.WriteAttributeString(reader.Name, reader.Value);
                            }

                            break;
                        case XmlNodeType.Text:
                            if (skip == 0)
                            {
                                writer.WriteString(reader.Value);
                            }
                            break;
                        case XmlNodeType.XmlDeclaration:
                        case XmlNodeType.ProcessingInstruction:
                            if (skip == 0)
                            {
                                writer.WriteProcessingInstruction(reader.Name, reader.Value);
                            }   
                            break;
                        case XmlNodeType.Comment:
                            if (skip == 0)
                            {
                                writer.WriteComment(reader.Value);
                            }
                            break;
                        case XmlNodeType.EndElement:
                            if (skip == 0)
                            {
                                writer.WriteFullEndElement();
                            }
                            skip -= reader.Name.Equals(RemoveMe) ? 1 : 0;
                            if (skip < 0)
                            {
                                throw new Exception("wrong sequence");
                            }
                            break;
                    }
                }

            }
        }

【讨论】:

  • 答案已更新,再次感谢@JLRishe 的宝贵建议
  • 您的代码运行良好。这正是我所需要的,我感谢你。关于序列的问题,是否有可能无法正确排序序列?除了损坏的 XML 吗?
  • 我也这么认为:只有损坏的 xml。再次感谢您的反馈。
【解决方案2】:

这听起来像是XSLT 的工作。

XSL 变换(RemoveElement.xslt):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="EL_TO_BE_REMOVED" />

</xsl:stylesheet>

执行转换的 C# 代码:

var transform = new XslCompiledTransform();
transform.Load("xslt/path/RemoveElement.xslt");

transform.Transform("input/xml/path/inputFile.xml", "output/xml/path/outputFile.xml");

【讨论】:

  • @MachineLearning 我认为这只是 XSLT 通常如何执行的概述,并不基于 XSLT 规范 (AFAIK) 中的任何内容。我不知道 .NET 的 XSLT 处理器是如何实现的,但由于它设计用于对输入和输出流进行操作,因此它可能会以一种相当聪明的方式来实现。至少值得一试。
猜你喜欢
  • 2012-05-15
  • 1970-01-01
  • 2021-02-04
  • 2013-08-02
  • 2015-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多