- Same Old Data
- The Basic Edit Example
- Putting It Together
- Example Web Interface
The Basic Edit Example
XML itself can be a very tedious data to editand even more so to code an editor for at times. Listing 1 gives way to some other niceties of .NET and demonstrates quickly how an edit might occur using the XmlDocument class from the System.Xml namespace.
Listing 1: A Simple Edit
<%@Page language="C#" %> <%@Import Namespace="System.Web.UI.HtmlControls" %> <%@Import Namespace="System.Web.UI.WebControls" %> <%@Import Namespace="System.Xml" %> <html> <script runat="server"> void SaveIt(Object source, EventArgs e) { // Create XmlDocument and load the XML from file XmlDocument myXmlDocument = (XmlDocument) Application["data"]; if (myXmlDocument == null) { msg.InnerHtml += "Loading xml data into the [ccc] Application state<br>"; myXmlDocument = new XmlDocument(); myXmlDocument.Load(Server.MapPath("/contacts.xml")); Application.Lock(); Application["data"] = myXmlDocument; Application.UnLock(); } XmlNode oNode = myXmlDocument.DocumentElement; XmlNodeList oList = oNode.SelectNodes("./contacts/entry"); foreach (XmlNode oEntry in oList) { XmlNode oName = oEntry.SelectSingleNode("./name"); String szLastName, szFirstName, szMidName; String szId = oEntry.Attributes["id"].Value; szLastName = Request.Form["lastname_" + szId]; szFirstName = Request.Form["firstname_" + szId]; szMidName = Request.Form["middlename_" + szId]; oName.Attributes["first"].Value = szFirstName; oName.Attributes["middle"].Value = szMidName; oName.Attributes["last"].Value = szLastName; msg.InnerHtml += szFirstName + " " + szMidName + " " + szLastName + "<br>"; } // Save over the original document. myXmlDocument.Save(Server.MapPath("/contacts.xml")); msg.InnerHtml += "<br> Saved edit to file<br>[ccc] <a href=\"/test2.aspx\">click here</a>"; } void Page_Load(Object sender, EventArgs e) { if (!IsPostBack) { msg.InnerHtml += "Now I'm here<br>"; // Create XmlDocument and load the XML from file XmlDocument myXmlDocument = (XmlDocument)[ccc] Application["data"]; if (myXmlDocument == null) { msg.InnerHtml += "Loading xml data into [ccc] the Application state<br>"; myXmlDocument = new XmlDocument(); myXmlDocument.Load(Server.MapPath("/contacts.xml")); Application.Lock(); Application["data"] = myXmlDocument; Application.UnLock(); } XmlNode oNode = myXmlDocument.DocumentElement; XmlNodeList oList = oNode.SelectNodes("./contacts/entry"); foreach (XmlNode oEntry in oList) { XmlNode oName = oEntry.SelectSingleNode("./name"); XmlAttributeCollection oAttributes = oName.Attributes; HtmlTableCell oTd = new HtmlTableCell(); String szId = oEntry.Attributes["id"].Value; TextBox oFirst = new TextBox(); oFirst.Text = oAttributes["first"].Value; oFirst.ID = "firstname_" + szId; TextBox oMiddle = new TextBox(); oMiddle.Text = oAttributes["middle"].Value; oMiddle.ID = "middlename_" + szId; TextBox oLast = new TextBox(); oLast.Text = oAttributes["last"].Value; oLast.ID = "lastname_" + szId; oTd.Controls.Add(oFirst); oTd.Controls.Add(oMiddle); oTd.Controls.Add(oLast); HtmlTableRow oTr = new HtmlTableRow(); oTr.Cells.Add(oTd); oTable.Rows.Add(oTr); } } } </script> <body> <span id="msg" runat="server"></span> <form method="POST" runat="server"> <table id="oTable" runat="server" /> <input type="hidden" value="0" id="entryctr" runat="server" /> <input type="submit" value="save" name="save" runat="server" OnServerClick="SaveIt"> </form> </body> </html>
Listing 1 simply allows a person to edit any values of the <name /> node. The Page_Load event handles the outputting a form for an edit, and the SaveIt event handles the form submittal. This is accomplished by using the server-side capabilities of .NET to render forms and carry the state for us. It's very handy feature, and it saves lots of work when building a UI for any sort of user data input.
NOTE
In the form, ASP.NET generates an interesting hidden variable that looks like this:
<input type="hidden" name="__VIEWSTATE" value=[ccc] "dDw2MzgzODY1OTU7dDw7bDxpPDI+Oz47bDx0PHA8bDxpbm5lcmh0bWw7PjtsPHcwMHQh[ccc] IHcwMHQhOz4+Ozs+Oz4+Oz4=" />
Don't be alarmed: This appears to be nothing more than a hash of the form's current state. This hidden input helps you avoid having to code even more if statements to test whether those text boxes should actually be updated. Notice the use of System.Web.UI.* to help simplify the grunt work of the html form. This is much preferred over the old ASP way of handling forms. As compared to the current style of kludging together some if statements to test values of form inputs, System.Web.UI allows developers to build clean pages. There is far more to building pages with the System.Web.UI namespace, but that needs to be addressed on its own.
Let's take a closer look at the SaveIt event. It contains the magical steps needed to actually complete editing on an XML document.
void SaveIt(Object source, EventArgs e) {
First we attempt to load the document from the global application state. Keeping the XmlDocument stored in the application state allows us to avoid any collisions when saving to the file system. You never really know when another thread will release a lock on the XML file and when a design based on opening and closing the XML file's every request will be slow and error-prone. The Lock() and Unlock() methods of the application state protect the changes from other threads as well.
XmlDocument myXmlDocument = (XmlDocument) Application["data"]; if (myXmlDocument == null) { msg.InnerHtml += "Loading xml data into the " + " Application state<br>"; myXmlDocument = new XmlDocument(); myXmlDocument.Load(Server.MapPath("/contacts.xml")); Application.Lock(); Application["data"] = myXmlDocument; Application.UnLock(); }
Next we select a list of nodes that might possibly be edited, and loop over every object. Note: There are more efficient ways to accomplish such a thing for large sets of data, but they would complicate the simplicity of this example. Refer to common Web application practices and your documentation if you would like to further extend this example for efficiency.
XmlNode oNode = myXmlDocument.DocumentElement; XmlNodeList oList = oNode.SelectNodes("./contacts/entry"); foreach (XmlNode oEntry in oList) {
Simply select the name node of the current entry, which will have an edit applied to it.
XmlNode oName = oEntry.SelectSingleNode("./name"); String szLastName, szFirstName, szMidName;
Here is some of that "magic" that I spoke of earlier. To keep all the node edits properly aligned, the <entry> ID attribute is appended to the input element's name. Thus, there are no worries in accidentally writing data to the wrong node.
String szId = oEntry.Attributes["id"].Value; szLastName = Request.Form["lastname_" + szId; szFirstName = Request.Form["firstname_" + szId; szMidName = Request.Form["middlename_" +szId;
Next we set the attributes of the selected <name /> node, which is all based on the <entry> node's attribute ID. Also, we output some debug information to the <span> element in the HTML to let us know what changes were applied.
oName.Attributes["first"].Value = szFirstName; oName.Attributes["middle"].Value = szMidName; oName.Attributes["last"].Value = szLastName; msg.InnerHtml += szFirstName + " " + szMidName + " " + [ccc] szLastName + "<br>"; }
The final step is to complete the edit. The save is done on the original file to maintain persistence of your changes. That is what this whole exercise is about, at least. Note: If you wanted to make a backup, you would just call .Save with the filename of choice before any edits are done. Beware of any directory security settings because NT can preclude the process from actually creating/saving a new file in a directory.
myXmlDocument.Save(Server.MapPath("/contacts.xml")); }
At this point, the changes are saved to disk. The actual save process is not too bad. It's always the interface that is bothersome, but as you saw previously, much of the hard work can be handled automatically by .NET HtmlControls functionality. At this stage, I'm sure you're wondering, "What about adding and deleting nodes?" Well, let's take what we now understand about XML editing in .NET and apply it to a more abstract approach.