【问题标题】:How to delete an invalid XML node from XML document when one of its attributes contains invalid data当 XML 文档的属性之一包含无效数据时,如何从 XML 文档中删除无效的 XML 节点
【发布时间】:2023-03-16 17:32:01
【问题描述】:

考虑以下 XML 文档:

<?xml version="1.0" encoding="iso-8859-1" ?>
<a>
    <b>
        <c1 description="abc123" /> 
        <c2 description="bbbasdasdbc123" /> 
        <c3 description="cccbasdasdc123" /> 
    </b>
    <b>
        <c1 description="abc123" /> 
        <c2 description="bbbasdasdbc123" /> 
        <c3 description="cccbasdasdc123" /> 
        <c4 description="abc123"" />    
        <c5 description="bbbasdasdbc123" /> 
        <c6 description="cccbasdasdc123" /> 
    </b>
    <b>
        <c1 description="abcaslkjkl123" weight="10" />
    </b>
</a>

目前这个 XML 文档是无效的,在 Firefox 中它指向有问题的行:Line 12 col 27... 即额外的双引号。 双引号不是这里的问题。错误的原因可能是导致 XML 文档无效的任何原因。

关键是当我尝试加载 XML 文档时会发生错误 - 我从中知道行号和列... - 之后我别无选择,只能将文件标记为 errored-do-something-以后再说吧。

我想做的是删除封装违规行的&lt;b&gt; 节点(或将其提取以供以后进一步的错误处理)

即删除

    <b>
        <c1 description="abc123" /> 
        <c2 description="bbbasdasdbc123" /> 
        <c3 description="cccbasdasdc123" /> 
        <c4 description="abc123"" />    
        <c5 description="bbbasdasdbc123" /> 
        <c6 description="cccbasdasdc123" /> 
    </b>

离开

<?xml version="1.0" encoding="iso-8859-1" ?>
<a>
    <b>
        <c1 description="abc123" /> 
        <c2 description="bbbasdasdbc123" /> 
        <c3 description="cccbasdasdc123" /> 
    </b>
    <b>
        <c1 description="abcaslkjkl123" weight="10" />
    </b>
</a>

XML 可以很大

我已经调查了这些最终导致我使用 File.ReadLines(sourceXMLFile).Take(...) 等

How to read a text file reversely with iterator in C#

Get last 10 lines of very large text file > 10GB

https://msdn.microsoft.com/en-us/library/w5aahf2a%28v=vs.110%29.aspx

并且事先使用模式来验证 XML 不是一种选择 (http://www.codeguru.com/csharp/csharp/cs_data/xml/article.php/c6737/Validation-of-XML-with-XSD.htm)。

在知道有问题的行号后,我已经考虑过尝试解决这个问题的方法,并想出了这个:

    public void ProcessXMLFile(string sourceXMLFile, string errorFile)
    {
        XmlDocument xmlDocument = new XmlDocument();

        string outputFile1 = @"c:\temp\f1.txt";
        string outputFile2 = @"c:\temp\f2.txt";

        string soughtOpeningNode = "<b>";
        string soughtClosingNode = "</b>";

        string firstPart = "";
        string secondPart = "";
        int lastNode = 0;
        int firstNode = 0;


        try
        {
            xmlDocument.Load(sourceXMLFile);
        }
        catch (XmlException ex)
        {
            int offendingLineNumber = ex.LineNumber;

            // Create the first part of the file that comprises everything upto and including the line that caused the error
            using (StreamWriter f1 = new StreamWriter(outputFile1))
            {
                firstPart = string.Join("\r\n", File.ReadLines(sourceXMLFile).Take(offendingLineNumber));
                f1.WriteLine(firstPart);
                lastNode = firstPart.LastIndexOf(soughtOpeningNode);
            }

            // Create the file that contains the remainder of the original file starting after the line number that caused the error
            using (StreamWriter f2 = new StreamWriter(outputFile2))
            {
                secondPart = string.Join("\r\n", File.ReadLines(sourceXMLFile).Skip(offendingLineNumber));
                f2.WriteLine(secondPart);
                firstNode = secondPart.IndexOf(soughtClosingNode);
            }

            // Create the XML file without the node whose child caused the error...
            using (StreamWriter d1 = new StreamWriter(sourceXMLFile))
            {
                d1.WriteLine(firstPart.Substring(0, lastNode));
                d1.WriteLine(secondPart.Substring(firstNode + soughtOpeningNode.Length + 1));
            }

            // Write the node that contained the offending line number for later processing
            using (StreamWriter d1 = new StreamWriter(errorFile, true))
            {
                d1.WriteLine(firstPart.Substring(lastNode));
                d1.WriteLine(secondPart.Substring(0, firstNode + soughtClosingNode.Length + 1));
            }

            File.Delete(outputFile1);
            File.Delete(outputFile2);

            ProcessXMLFile(sourceXMLFile, errorFile);
        }
    }

然后开始:

ProcessXMLFile(@"c:\temp\myBigFile.xml", @"c:\temp\myBigFile-errors.txt");

那么我的问题:

  1. 这可行,但有更好的方法吗?
  2. 在处理包含许多错误的 XML 文件 (c70Mb) 时,它最终会耗尽内存(任务管理器显示内存使用率在 16Gb m/c 上一直攀升至 99%)。
  3. 即使我强制例程完成内存仍保持在 99% 并且仅在 VS2010 停止时下降,那么如何才能提高内存使用效率?

指针将不胜感激。

赛。

【问题讨论】:

  • 使用 StreamReader 之类的流类和 ReadLine() 方法来获取需要编辑的行。
  • 嗨@jdweng。这不会做我所追求的,即如何切出包含违规行的节点。
  • 您需要打开第二个流以保存更改的结果。
  • 嗨,是的,刚刚发现那里的逻辑错误。我试图成为一个聪明的亚历克。将发布适用于原始文件的新代码(原始文件会变小,而结果会变大)。很抱歉打扰您。

标签: c# xml validation memory-management


【解决方案1】:

这似乎是一件很狡猾的事情。通常,如果 XML 文件格式不正确,则无法将其作为 XML 文件读取。错误消息中出现的行和列并不一定告诉您“这是错误的位置”,它只是告诉您 XML 解析器在什么时候无法理解文件并放弃了。

因此,您最多只能处理 XML 文件中可能出现的错误的一个子集。在您的情况下,您可能知道您希望看到什么样的错误(例如,未正确编码的元素中的数据)在这种情况下,尝试剥离封闭元素可能是有意义的,但仍会修复创建输入文件的代码。

现在解决您的具体问题,您的代码似乎以合理的方式执行此操作,但如果您确切知道您期望的错误类型(例如,您的示例中的双引号)也许您可以在文件中搜索那些而不是反复尝试将其解析为 XML 并处理产生的错误。

就内存使用而言,当您进行 Release 构建并在调试器之外运行它时,您是否还有问题?我发现在调试器下内存使用持续增长,大概是因为垃圾收集没有那么积极,但是当我运行发布版本时它保持稳定。

【讨论】:

  • 嗨,大卫,感谢您的 cmets。同意我不应该处理格式错误的 XML,但是,我必须允许这种情况。很高兴听到我的方法是合理的。哦,我通过将功能放入一个类、调用和 disposing() 来解决内存问题......现在处理 70Mb XML 很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-05
  • 2020-01-15
  • 1970-01-01
  • 2012-06-06
  • 1970-01-01
  • 2019-06-17
  • 1970-01-01
相关资源
最近更新 更多