替换大型XML文件的一部分
本文关键字:一部分 文件 XML 大型 替换 | 更新日期: 2023-09-27 18:02:47
我有一个大的XML文件,我需要用另一个元素替换具有某个名称的元素(以及所有内部元素(。例如,如果此元素e
:
<a>
<b></b>
<e>
<b></b>
<c></c>
</e>
</a>
将e
替换为elem
后:
<a>
<b></b>
<elem></elem>
</a>
更新:我尝试使用XDocument
,但xml大小超过2gb,并且我有SystemOutOfMemoryException
更新2:我的代码,但xml不能转换
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();
更新3:
<a>
<b></b>
<e>
<b></b>
<c></c>
</e>
<i>
<e>
<b></b>
<c></c>
</e>
</i>
</a>
从这篇博客文章中获得灵感,您基本上可以像示例代码一样,将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
试试这个(看到C#标签:D(:
XElement elem = new XElement("elem");
IEnumerable<XElement> listElementsToBeReplaced = xDocument.Descendants("e");
foreach (XElement replaceElement in listElementsToBeReplaced)
{
replaceElement.AddAfterSelf(elem);
}
listElementsToBeReplaced.Remove();
我会用正则表达式替换它,将e元素与其所有内容匹配,并以结束标记结束,然后用新的elem元素替换它。通过这种方式,您可以在任何具有搜索/替换功能的编辑器中进行搜索/替换,该编辑器支持正则表达式并以任何语言编程。
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
// 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"));
}
我没有衡量这场比赛的表现,但有传言说这场比赛差别不大。
XPath语法,如果需要:http://www.w3schools.com/xpath/xpath_syntax.asp