我有大型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();
答案 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