C # Find and Replace XML Nodes

Edit: I decided to use the LINQ to XML approach (see answer below), which was recommended, and everything works EXCEPT, that I can not replace the replaced records with records from the incremental file. I managed to get the program to work by simply deleting the full node file and then adding the incremental node. Is there a way to just replace them? Also, although this solution is very nice, is there a way to reduce memory usage without losing LINQ code? This solution may still work, but I would like to sacrifice time to reduce memory usage.


I am trying to take two XML files (full file and incremental file) and merge them together. The XML file is as follows:

<List> <Records> <Person id="001" recordaction="add"> ... </Person> </Records> </List> 

The recordaction attribute can also be "chg" for changes or "del" for deletion. The main logic of my program:

1) Read the full file in the XmlDocument.

2) Read the incremental file in the XmlDocument, select the nodes using XmlDocument.SelectNodes (), put these nodes in the dictionary to simplify the search.

3) Select all nodes in the full file, cycle through and check them against the dictionary containing incremental entries. If recordaction = "chg" or "del" add a node to the list, and then remove all the nodes from the XmlNodeList that are in this list. Finally, add recordaction = "chg" or "add" entries from the incremental file to the full file.

4) Save the XML file.

I am having serious problems with step 3. Here is the code for this function:

 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; } } } 

Only all incremental entries that were selected from the incremental file are transferred to the XmlNodeList, and the dictionary is only the same nodes, but key'd on the identifier, so I did not have to iterate over all incremental entries every time. Now the program is dying at the "Delete selected entries" stage due to indexing beyond. I am sure that "Add to incremental records" does not work either. Any ideas? In addition, some suggestions for improving efficiency will be pleasant. I could run into a problem because I was reading into a 250 megabyte file that takes off up to 750 MB in memory, so I was wondering if there is an easier way to switch node-by-node to the full file. Thanks!

+4
source share
1 answer

Here is an example of how you can accomplish this with LINQ-to-XML. No dictionary required:

 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"); 

Please let me know if you have problems with the launch and I will edit and fix it. I am sure this is correct, but there are currently no VS available.

EDIT: The case of "chg" for using personToChange.ReplaceWith (person), rather than "personToChange = person", has been fixed. The latter does not replace anything, since it simply shifts the link from the underlying document.

+5
source

All Articles