You need to inform one or more classes or components that a node in an XML document has been inserted, removed, or had its value changed.
In order to track changes to an active XML document, subscribe to the
events published by the
XmlDocument
class.
XmlDocument
publishes events for node creation,
insertion, and removal for both the pre- and post-conditions of these
actions. In the following example, we have a number of event handlers
defined in the same scope as the DetectXMLChanges
method, but they could just as easily be callbacks to functions on
other classes that are interested in the manipulation of the live XML
document.
DetectXMLChanges
loads an XML fragment we define in the
method, wires up the event handlers for the node events, adds,
changes, and removes some nodes to trigger the events, then writes
out the resulting XML:
public static void DetectXMLChanges( ) { string xmlFragment = "<?xml version='1.0'?>" + "<!-- My sample XML -->" + "<?pi myProcessingInstruction?>" + "<Root>" + "<Node1 nodeId='1'>First Node</Node1>" + "<Node2 nodeId='2'>Second Node</Node2>" + "<Node3 nodeId='3'>Third Node</Node3>" + @"<Node4><![CDATA[<>&']]></Node4>" + "</Root>"; XmlDocument doc = new XmlDocument( ); doc.LoadXml(xmlFragment); //Create the event handlers. doc.NodeChanging += new XmlNodeChangedEventHandler(NodeChangingEvent); doc.NodeChanged += new XmlNodeChangedEventHandler(NodeChangedEvent); doc.NodeInserting += new XmlNodeChangedEventHandler(NodeInsertingEvent); doc.NodeInserted += new XmlNodeChangedEventHandler(NodeInsertedEvent); doc.NodeRemoving += new XmlNodeChangedEventHandler(NodeRemovingEvent); doc.NodeRemoved += new XmlNodeChangedEventHandler(NodeRemovedEvent); // Add a new element node. XmlElement elem = doc.CreateElement("Node5"); XmlText text = doc.CreateTextNode("Fifth Element"); doc.DocumentElement.AppendChild(elem); doc.DocumentElement.LastChild.AppendChild(text); // Change the first node doc.DocumentElement.FirstChild.InnerText = "1st Node"; // remove the fourth node XmlNodeList nodes = doc.DocumentElement.ChildNodes; foreach(XmlNode node in nodes) { if(node.Name == "Node4") { doc.DocumentElement.RemoveChild(node); break; } } // write out the new xml Console.WriteLine(doc.OuterXml); }
These are the event handlers from the XmlDocument
along with one formatting method, WriteNodeInfo
,
that takes an action string and gets the name and value of the node
being manipulated. All of the event handlers invoke this formatting
method, passing the corresponding action
string:
private static void WriteNodeInfo(string action, XmlNode node) { if (node.Value != null) { Console.WriteLine("Element: <{0}> {1} with value {2}", node.Name,action,node.Value); } else Console.WriteLine("Element: <{0}> {1} with null value", node.Name,action); } public static void NodeChangingEvent(object source, XmlNodeChangedEventArgs e) { WriteNodeInfo("changing",e.Node); } public static void NodeChangedEvent(object source, XmlNodeChangedEventArgs e) { WriteNodeInfo("changed",e.Node); } public static void NodeInsertingEvent(object source, XmlNodeChangedEventArgs e) { WriteNodeInfo("inserting",e.Node); } public static void NodeInsertedEvent(object source, XmlNodeChangedEventArgs e) { WriteNodeInfo("inserted",e.Node); } public static void NodeRemovingEvent(object source, XmlNodeChangedEventArgs e) { WriteNodeInfo("removing",e.Node); } public static void NodeRemovedEvent(object source, XmlNodeChangedEventArgs e) { WriteNodeInfo("removed",e.Node); }
The DetectXmlChanges
method results in the
following output:
Element: <Node5> inserting with null value Element: <Node5> inserted with null value Element: <#text> inserting with value Fifth Element Element: <#text> inserted with value Fifth Element Element: <#text> changing with value First Node Element: <#text> changed with value 1st Node Element: <Node4> removing with null value Element: <Node4> removed with null value <?xml version="1.0"?><!-- My sample XML --><?pi myProcessingInstruction?><Root>< Node1 nodeId="1">1st Node</Node1><Node2 nodeId="2">Second Node</Node2><Node3 nod eId="3">Third Node</Node3><Node5>Fifth Element</Node5></Root>
With an XmlDocument
,
you can traverse both forward and backward on the XML stream, as well
as use XPath
navigation to find nodes. If you are
just reading XML and not modifying it, and you have no need for
traversing backward through the nodes, you should avoid using
XmlDocument
, since
XmlTextReader
is faster for reading and
XmlTextWriter
is faster for writing (both have
less overhead than XmlDocument
). The .NET
Framework team did a nice job of giving XML processing flexibility,
but if you use a class with more functionality than you need, you
will pay the resulting performance penalty.