c#查找和替换XML节点
本文关键字:XML 节点 替换 查找 | 更新日期: 2023-09-27 18:03:27
Edit:我决定采用推荐的LINQ to XML方法(参见下面的答案),除了我不能用增量文件中的记录替换出更改的记录外,一切都有效。我设法通过删除完整的文件节点,然后添加增量节点,使程序工作。有没有办法换掉它们呢?另外,虽然这个解决方案非常好,但有没有办法在不丢失LINQ代码的情况下减少内存使用?这个解决方案可能仍然有效,但我愿意牺牲时间来降低内存使用。
我试图采取两个XML文件(一个完整的文件和增量文件),并将它们合并在一起。XML文件看起来像这样:
<List>
<Records>
<Person id="001" recordaction="add">
...
</Person>
</Records>
</List>
recordaction属性也可以是"chg"用于更改或"del"用于删除。程序的基本逻辑是:
1)将整个文件读入XmlDocument。
2)将增量文件读入XmlDocument,使用XmlDocument. selectnodes()选择节点,将这些节点放入字典中以便于搜索。
3)选择完整文件中的所有节点,循环遍历并根据包含增量记录的字典检查每个节点。如果recordaction="chg"或"del"将节点添加到列表中,则从该列表中的XmlNodeList中删除所有节点。最后,将recordaction="chg"或"add"记录从增量文件添加到完整文件中。
4)保存XML文件。我在第三步遇到了一些严重的问题。下面是该函数的代码:
private void ProcessChanges(XmlNodeList nodeList, Dictionary<string, XmlNode> dictNodes)
{
XmlNode lastNode = null;
XmlNode currentNode = null;
List<XmlNode> nodesToBeDeleted = new List<XmlNode>();
// If node from full file matches to incremental record and is change or delete,
// mark full record to be deleted.
foreach (XmlNode fullNode in fullDocument.SelectNodes("/List/Records/Person"))
{
dictNodes.TryGetValue(fullNode.Attributes[0].Value, out currentNode);
if (currentNode != null)
{
if (currentNode.Attributes["recordaction"].Value == "chg"
|| currentNode.Attributes["recordaction"].Value == "del")
{
nodesToBeDeleted.Add(currentNode);
}
}
lastNode = fullNode;
}
// Delete marked records
for (int i = nodeList.Count - 1; i >= 0; i--)
{
if(nodesToBeDeleted.Contains(nodeList[i]))
{
nodeList[i].ParentNode.RemoveChild(nodesToBeDeleted[i]);
}
}
// Add in the incremental records to the new full file for records marked add or change.
foreach (XmlNode weeklyNode in nodeList)
{
if (weeklyNode.Attributes["recordaction"].Value == "add"
|| weeklyNode.Attributes["recordaction"].Value == "chg")
{
fullDocument.InsertAfter(weeklyNode, lastNode);
lastNode = weeklyNode;
}
}
}
传入的XmlNodeList只是从增量文件中选择出来的所有增量记录,字典只是那些相同的节点,但键在id上,所以我不必每次都循环遍历所有增量记录。目前,由于索引越界,程序正在"删除标记记录"阶段死亡。我很确定"添加增量记录"也不起作用。什么好主意吗?也有一些建议,使这更有效,将是很好的。我可能会遇到一个问题,因为它读取的是一个250MB的文件,内存会膨胀到750MB,所以我想知道是否有一种更简单的方法来逐个节点地读取整个文件。谢谢!
下面是如何使用LINQ-to-XML完成此任务的示例。不需要字典:
using System.Xml.Linq;
// Load the main and incremental xml files into XDocuments
XDocument fullFile = XDocument.Load("fullfilename.xml");
XDocument incrementalFile = XDocument.Load("incrementalfilename.xml");
// For each Person in the incremental file
foreach (XElement person in incrementalFile.Descendants("Person")) {
// If the person should be added to the full file
if (person.Attribute("recordaction").Value == "add") {
fullFile.Element("List").Element("Records").Add(person); // Add him
}
// Else the person already exists in the full file
else {
// Find the element of the Person to delete or change
var personToChange =
(from p in fullFile.Descendants("Person")
where p.Attribute("id").Value == person.Attribute("id").Value
select p).Single();
// Perform the appropriate operation
switch (person.Attribute("recordaction").Value) {
case "chg":
personToChange.ReplaceWith(person);
break;
case "del":
personToChange.Remove();
break;
default:
throw new ApplicationException("Unrecognized attribute");
}
}
}// end foreach
// Save the changes to the full file
fullFile.Save("fullfilename.xml");
请让我知道如果你有任何问题运行它,我会编辑和修复它。我很确定这是正确的,但目前没有VS可用。
编辑:修正了"chg"
的情况下使用personToChange.ReplaceWith(person)
而不是personToChange = person
。后者不替换任何东西,因为它只是将引用从底层文档移开。