替换部分大型XML文件

时间:2015-07-14 12:00:02

标签: c# xml

我有大型XML文件,我需要用另一个元素替换一些名称(和所有内部元素)的元素。例如 - 如果此元素e

<a>
<b></b>
<e>
   <b></b>
   <c></c>
</e>
</a>

替换e的{​​{1}}后:

elem

更新:我尝试使用<a> <b></b> <elem></elem> </a> ,但xml大小超过2gb,我有XDocument

update2:我的代码,但xml没有转换

SystemOutOfMemoryException

更新3:

XmlReader reader = XmlReader.Create("xml_file.xml");
XmlWriter wr = XmlWriter.Create(Console.Out);
while (reader.Read())
   {
       if (reader.NodeType == XmlNodeType.Element && reader.Name == "e")
       {
           wr.WriteElementString("elem", "val1");
           reader.ReadSubtree();
       }
            wr.WriteNode(reader, false);
   }
wr.Close();

5 个答案:

答案 0 :(得分:3)

this blog post中获取灵感,您基本上可以直接将XmlReader的内容直接传输到XmlWriter,类似于您的示例代码,但处理所有节点类型。在示例代码中使用WriteNode将添加节点和所有子节点,因此您将无法处理源XML中的每个后代。

此外,您需要确保阅读要跳过的元素的末尾 - ReadSubtree为此创建XmlReader,但它实际上并没有读取任何内容。您需要确保将其读取到最后。

生成的代码可能如下所示:

using (var reader = XmlReader.Create(new StringReader(xml), rs))
using (var writer = XmlWriter.Create(Console.Out, ws))
{
    while (reader.Read())
    {
        switch (reader.NodeType)
        {
            case XmlNodeType.Element:
                var subTreeReader = reader.ReadSubtree();
                if (HandleElement(reader, writer))
                {
                    ReadToEnd(subTreeReader);
                }
                else
                {
                    writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
                    writer.WriteAttributes(reader, true);
                    if (reader.IsEmptyElement)
                    {
                        writer.WriteEndElement();
                    }
                }
                break;
            case XmlNodeType.Text:
                writer.WriteString(reader.Value);
                break;
            case XmlNodeType.Whitespace:
            case XmlNodeType.SignificantWhitespace:
                writer.WriteWhitespace(reader.Value);
                break;
            case XmlNodeType.CDATA:
                writer.WriteCData(reader.Value);
                break;
            case XmlNodeType.EntityReference:
                writer.WriteEntityRef(reader.Name);
                break;
            case XmlNodeType.XmlDeclaration:
            case XmlNodeType.ProcessingInstruction:
                writer.WriteProcessingInstruction(reader.Name, reader.Value);
                break;
            case XmlNodeType.DocumentType:
                writer.WriteDocType(reader.Name, reader.GetAttribute("PUBLIC"), reader.GetAttribute("SYSTEM"), reader.Value);
                break;
            case XmlNodeType.Comment:
                writer.WriteComment(reader.Value);
                break;
            case XmlNodeType.EndElement:
                writer.WriteFullEndElement();
                break;
        }
    }    
}

private static void ReadToEnd(XmlReader reader)
{
    while (!reader.EOF)
    {
        reader.Read();
    }
}

显然把你的逻辑放在HandleElement里面,如果元素被处理则返回true(因此要被忽略)。示例代码中逻辑的实现将是:

private static bool HandleElement(XmlReader reader, XmlWriter writer)
{
    if (reader.Name == "e")
    {
        writer.WriteElementString("element", "val1");
        return true;
    }

    return false;
}

这是一个有效的演示:https://dotnetfiddle.net/FFIBU4

答案 1 :(得分:1)

试试这个(看到C#标签:D):

        XElement elem = new XElement("elem");
        IEnumerable<XElement> listElementsToBeReplaced = xDocument.Descendants("e");
        foreach (XElement replaceElement in listElementsToBeReplaced)
        {
            replaceElement.AddAfterSelf(elem);
        }
        listElementsToBeReplaced.Remove();

答案 2 :(得分:0)

我会用正则表达式替换它,将e元素与其所有内容匹配,并以结束标记结束,并将其替换为新的elem元素。 通过这种方式,您可以在任何支持正则表达式的搜索/替换编辑器中以任何语言编程。

答案 3 :(得分:0)

string xml = @"<a>
<b></b>
<e>
<b></b>
<c></c>
</e>
</a>";
string patten = @"<e[^>]*>[\s\S]*?(((?'Open'<e[^>]*>)[\s\S]*?)+((?'-Open'</e>)[\s\S]*?)+)*(?(Open)(?!))</e>";
Console.WriteLine(Regex.Replace(xml,patten,"<ele></ele>"));

使用正则表达式,也可以使用LinqToXml

答案 4 :(得分:0)

// example data:
XDocument xmldoc = XDocument.Parse(
@"
<a>
<b></b>
<e>
   <b></b>
   <c></c>
</e>
<c />
<e>
   <b></b>
   <c></c>
   <c></c>
</e>
</a>
");
            // you can use xpath, then you need to add:
            // using System.Xml.XPath;
            List<XElement> elementsToReplace = xmldoc.XPathSelectElements("a/e").ToList();

            // or pure linq-to-sql:
            // elementsToReplace = xmldoc.Elements("a").Elements("e").ToList();

            foreach (XElement elem in elementsToReplace)
            {
                // setting Value of XElement to an empty string causes the resulting xml to look like this:
                // <elem></elem>
                // and not like this:
                // <elem />
                elem.ReplaceWith(new XElement("elem", ""));
                // if you don't mind self closing tags, then:
                // elem.ReplaceWith(new XElement("elem"));
            }

我没有衡量表现,但rumour has it差异不是很显着。

XPath语法,如果您需要它:http://www.w3schools.com/xpath/xpath_syntax.asp