【问题标题】:Edit a large XML file编辑大型 XML 文件
【发布时间】:2018-02-02 02:00:06
【问题描述】:

我有一个 3GB 的 XML 文件。我需要将节点作为另一个的子节点移动。将大文件加载为XmlDocument 效率不高。我看到XmlReader 是另一种方法,但不确定它在我的场景中将如何工作以及我应该使用哪些其他类来执行此操作。

我需要将所有别名节点移动到其相关的客户>名称节点。

<customer>
<name><first>Robert</first></name>
<alias>Rob</alias>
</customer>

【问题讨论】:

  • 一个 3 giga 字节的 XML 文件?或许将这个文件拆分成更小的文件会是很好的第一步。
  • 64 位应用程序是否允许您使用 XDocument 读取 XML?

标签: c# xml


【解决方案1】:

我不太了解您想要执行什么转换,但假设@dbc 的猜测是正确的,您可以使用这样的流式 XSLT 3.0 处理器来完成:

<xsl:transform version="3.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:mode streamable="yes" on-no-match="shallow-copy">

<xsl:template match="customer">
  <xsl:apply-templates select="copy-of(.)" mode="local"/>
</xsl:template>

<xsl:mode name="local" streamable="no" on-no-match="shallow-copy"/>

<xsl:template match="name" mode="local">
  <name>
    <xsl:apply-templates mode="local"/>
    <xsl:copy-of select="../alias"/>
  </name>
</xsl:template>

<xsl:template match="alias" mode="local"/>

</xsl:transform>

这里发生的情况是,所有内容都以纯流模式(标签对标签)复制,直到我们遇到客户元素。当我们遇到客户元素时,我们会在内存中制作该元素的副本,并使用传统的非流式转换在本地对其进行转换。因此,所需的内存量刚好足以容纳最大的客户元素。

【讨论】:

    【解决方案2】:

    您可以做的是采用 Mark Fussell 的文章 Combining the XmlReader and XmlWriter classes for simple streaming transformations 中将 XmlReader 流式传输到 XmlWriter 的基本逻辑,将您的 3GB 文件转换为修改后的文件,其中 &lt;alias&gt; 节点已被重新定位到&lt;name&gt; 节点。 this answerAutomating replacing tables from external files 中给出了使用这种流转换的示例。

    使用该答案作为基础,从中获取 XmlReaderExtensionsXmlWriterExtensionsXmlStreamingEditorBaseXmlStreamingEditor 类并创建 XmlStreamingEditor 的子类以创建 CustomerAliasXmlEditor,如下所示:

    class CustomerAliasXmlEditor : XmlStreamingEditor
    {
        // Confirm that the <customer> element is not in any namespace.
        static readonly XNamespace customerNamespace = ""; 
        
        public static void TransformFromTo(string fromFilePath, XmlReaderSettings readerSettings, string toFilePath, XmlWriterSettings writerSettings)
        {
            using (var xmlReader = XmlReader.Create(fromFilePath, readerSettings))
            using (var xmlWriter = XmlWriter.Create(toFilePath, writerSettings))
            {
                new CustomerAliasXmlEditor(xmlReader, xmlWriter).Process();
            }
        }
    
        public CustomerAliasXmlEditor(XmlReader reader, XmlWriter writer)
            : base(reader, writer, ShouldTransform, Transform)
        {
        }
    
        static bool ShouldTransform(XmlReader reader)
        {
            return reader.GetElementName() == customerNamespace + "customer";
        }
    
        static void Transform(XmlReader from, XmlWriter to)
        {
            var customer = XElement.Load(from);
            var alias = customer.Element(customerNamespace + "alias");
            if (alias != null)
            {
                var name = customer.Element(customerNamespace + "name");
                if (name == null)
                {
                    name = new XElement(customerNamespace + "name");
                    customer.Add(name);
                }
                alias.Remove();
                name.Add(alias);
            }
            customer.WriteTo(to);
        }
    }
    

    如果fromFileName 是您当前的 3GB XML 文件的名称,toFileName 是输出转换后的 XML 的文件的名称,您可以这样做:

    var readerSettings = new XmlReaderSettings { IgnoreWhitespace = true };
    var writerSettings = new XmlWriterSettings { Indent = false}; // Or true if you prefer.
    
    CustomerAliasXmlEditor.TransformFromTo(fromFileName, readerSettings, toFileName, writerSettings);
    

    工作示例.Net fiddle 显示 XML

    <Root>
    <Item>
    <SubItem>
    <customer>
    <name><first>Robert</first></name>
    <alias>Rob</alias>
    </customer>
    </SubItem>
    </Item>
    <Item>
    </Root>
    

    转化为

    <Root>
      <Item>
        <SubItem>
          <customer>
            <name>
              <first>Robert</first>
              <alias>Rob</alias>
            </name>
          </customer>
        </SubItem>
      </Item>
      <Item>
    </Root>
    

    【讨论】:

    • 我非常喜欢您的解决方案,但我认为它对 xml 的结构做了一些假设,并且认为它不适用于大文件。我们应该从op.Suspecting xml 文件包含一个节点 。所以解决方案是创建一个写入 xml 文件,直到并包括 。对文件进行两次解析。第一次通过创建一组元素表用户名。第二遍写入组。文件太大,可能使用 XmlReader 基流的位置属性来定位每条记录。这个解决方案可能很慢,但会避免 mem err。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-10
    • 2019-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多