The tribal knowledge in dealing with XmlDocument in C# is that it takes the reference to a tree and its branches too literally. When pruning or adding to the tree, one must always take from the branch (node) from which it comes to do any operation.  Here are a few examples to work with the XmlDocument

Add Node With Attributes

string xml = @"<?xml version='1.0' encoding='utf-8'?>
   <menu>
    <sub name='A' value='p1.aspx'/>
    <sub name='B' value='p2.aspx'/>
    <sub name='C' value='p3.aspx'/>
   </menu>";'

XmlDocument originalXml = new XmlDocument();
originalXml.LoadXml(xml);

XmlNode menu = originalXml.SelectSingleNode("menu");

XmlNode newSub = originalXml.CreateNode(XmlNodeType.Element, "sub", null);

XmlAttribute xa = originalXml.CreateAttribute("Name");
xa.Value = "D";

XmlAttribute xb = originalXml.CreateAttribute("value");
xb.Value = "p4.aspx";

newSub.Attributes.Append(xa);
newSub.Attributes.Append(xb);

menu.AppendChild(newSub);

Console.WriteLine(originalXml.OuterXml.ToString());

The above code we had a static set of Xml, and added a node with two attributes. Here is the output (pretty printed after the fact and not by the code)

<?xml version="1.0" encoding="utf-8"?>
    <menu>
        <sub name="A" value="p1.aspx" />
        <sub name="B" value="p2.aspx" />
        <sub name="C" value="p3.aspx" />
        <sub Name="D" value="p4.aspx" />
     </menu>

Remove Node

Using the above snippet to remove the “B” node we will search for it then remove it

XmlNode bNode = originalXml.SelectSingleNode("descendant::sub[@name='B']");
if (bNode != null)
    menu.RemoveChild(bNode);
Console.WriteLine(originalXml.OuterXml.ToString());

Which produces this Output

<?xml version="1.0" encoding="utf-8"?>
    <menu>
        <sub name="A" value="p1.aspx" />
        <sub name="C" value="p3.aspx" />
        <sub Name="D" value="p4.aspx" />
    </menu>

Replace Xml Snippet

In this example we will simulate a user making a changes to an xml node say within an editor. All we know is that it has been changed, but we don’t know what. It could be either additions or subtractions of attributes and nodes; regardless it needs to replace an original item.

string xmlInitial =
@"<?xml version='1.0'?>
 <Rules>
  <OpenBalances function='ReOrderFifo'>
   <column name='SecurityID' used='True'/>
   <column name='COL2' used='False'>#@#</column>
   <column name='COL3' used='False'>#@#</column>
  </OpenBalances>
 <ClosedBalances/>
 </Rules>";

string xmlUser =
@"<OpenBalances function='ReOrderFifo' iAmNew='true'>
  <column name='SecurityID' used='True'/>
  <column name='COL3' used='True'>New Item</column>
  <column name='COL5' used='True'>Other Item</column>
 </OpenBalances>";

XmlDocument originalXml = new XmlDocument();
string targetNode = "descendant::*[name(.) ='OpenBalances']";

originalXml.LoadXml( xmlInitial );

// Simulate the selection of the subnode
// for the user to edit in the first nodes
// Rules.
XmlNode editNode = originalXml.SelectSingleNode(targetNode);

// Get a fragment and slide the changed data into it.
XmlDocumentFragment fragment = originalXml.CreateDocumentFragment();
fragment.InnerXml = xmlUser;

// Replace the contents of the editNode with the user fragment.
editNode.ParentNode.ReplaceChild(fragment, editNode);

Console.WriteLine(originalXml.OuterXml);

Here is the resulting output

<?xml version="1.0"?>
 <Rules>
  <OpenBalances function="ReOrderFifo" iAmNew="true">
   <column name="SecurityID" used="True" />
   <column name="COL3" used="True">New Item</column>
   <column name="COL5" used="True">Other Item</column>
  </OpenBalances>
 <ClosedBalances />
 </Rules>
Share