Changing an XmlDocument

In the previous example, you didn’t actually change the underlying document. In fact, there’s nothing there that you couldn’t have done with an XmlReader. Unlike an XmlReader, however, the DOM allows you to change an existing XML document.

Suppose you decided to stop validating the inventory records. In order to make this change, you would need to remove the DOCTYPE node from all of the XML files. How would you go about doing this?

The short answer is XmlNode.RemoveChild( ). This method removes the node passed in from the object tree. You can read in all the XML files in the current directory, and remove the XmlDocumentType node. Then you can serialize the file back out (with the extension .new so you don’t overwrite the original) and check that the DOCTYPE node is gone:

string currentDirectory = Environment.CurrentDirectory;
string [ ] files = Directory.GetFiles(currentDirectory, "*.xml");

foreach (string file in files) {
  XmlDocument document = new XmlDocument( );
  document.Load(file);
  XmlDocumentType documentType = document.DocumentType;
  document.RemoveChild(documentType);
  document.Save(file + ".new");
}

This process can be repeated with any type of XmlNode. For example, you could remove the inventory element, leaving an empty document, except for the XML declaration. Or you could use RemoveAll( ) to remove everything in the document entirely, while leaving the empty file in place:

document.Load(file);
document.RemoveAll( );

Tip

If you remove the document element, the document is no longer well-formed. XmlDocument.Save( ) will throw an XmlException.

A more common case, given our example, would be to change the quantity of a particular item in stock. If you look back at the purchase order DTD from Chapter 2, you can see that the item elements are identical. You could write a small program to read in the store inventory and a purchase order, and decrement the inventory by the number of items sold in the PO.

Let’s build the program, starting with a class called SellItems. To begin, because you’re dealing with the same inventory file for all of the purchase orders, you can just store it as an instance variable:

private XmlDocument inventory;

In the Main( ) method, all you need do is instantiate a new SellItems object, passing the list of purchase order files that appeared on the command line:

static void Main(string [ ] args) {
  new SellItems(args);
}

The constructor creates the inventory XmlDocument and loads it from a file:

private SellItems(string [ ] files) {
  inventory = new XmlDocument( );
  inventory.Load("inventory.xml");

Next, loop through the purchase order file names, calling SellItemsFromPoFile( ) for each one:

  foreach (string filename in files) {
    SellItemsFromPoFile(filename);
  }

Finally, save the inventory document with all changes:

  inventory.Save("inventory.xml");
}

The SellItemsFromPoFile( ) method will create and load an individual purchase order from the list. For efficiency, each purchase order XmlDocument shares the same XmlNameTable with the others, and with the inventory XmlDocument:

private void SellItemsFromPoFile(string filename) {
  XmlDocument po = new XmlDocument(inventory.NameTable);
  po.Load(filename);

This XPath expression selects each item element from the purchase order:

  XmlNodeList elements = po.SelectNodes("//items/item");

This loop calls SellItemsFromElement( ) for each item element that the XPath expression returned:

  foreach (XmlElement element in elements) {
    SellItemsFromElement(element);
  }
}

Next is SellItemsFromElement( ) itself, the method that actually decrements the inventory. First, you get the product code and the quantity sold from the purchase order’s item element:

private void SellItemsFromElement(XmlElement poItem) {
  string productCode = poItem.GetAttribute("productCode");
  int quantitySold = Int32.Parse(
    poItem.GetAttribute("quantity"));

Now, you search for the same product code in the inventory’s item elements. Again, XPath is discussed in the next chapter; for now, don’t worry too much about the XPath syntax:

  string xPathExpression = 
    "//items/item[@productCode='" + productCode + "']";
  XmlElement inventoryItem = 
    (XmlElement)inventory.SelectSingleNode(xPathExpression);

Warning

If the XPath expression does not return a single matching node, a NullReferenceException will be thrown. It might be smart to wrap this call in a try...catch block to handle this better, and avoid abnormal termination of the program.

Here you’re getting the quantity attribute from the inventory document, subtracting from it the amount in the purchase order document, and setting the inventory document’s quantity attribute to the new decremented amount:

  int quantity = Int32.Parse(inventoryItem.GetAttribute("quantity"));
  quantity -= quantitySold;
  inventoryItem.SetAttribute("quantity", quantity.ToString( ));
}

And that’s it, a simple inventory maintenance program. Granted, it’s not a good idea to keep your inventory in a flat XML file; but if you think of the various ways you can construct an XmlDocument, you could actually be reading XML from a relational database, or some sort of web service, or almost anything you can imagine.

Example 5-3 shows the complete program.

Example 5-3. A program to update inventory
using System;
using System.Xml;

public class SellItems {

  private XmlDocument inventory;

  static void Main(string [ ] args) {
    new SellItems(args);
  }

  private SellItems(string [ ] files) {
    XmlDocument inventory = new XmlDocument( );
    inventory.Load("inventory.xml");

    foreach (string filename in files) {
      SellItemsFromPoFile(filename);
    }

    inventory.Save("inventory.xml ");
  }

  private void SellItemsFromPoFile(string filename) {
    XmlDocument po = new XmlDocument(inventory.NameTable);
    po.Load(filename);

    XmlNodeList elements = po.SelectNodes("//items/item");
    foreach (XmlElement element in elements) {
      SellItemsFromElement(element);
    }
  }

  private void SellItemsFromElement(XmlElement poItem) {
    string productCode = poItem.GetAttribute("productCode");
    int quantitySold = Int32.Parse(
      poItem.GetAttribute("quantity"));
    string xPathExpression = 
      "//items/item[@productCode='" + productCode + "']";
    XmlElement inventoryItem = 
      (XmlElement)inventory.SelectSingleNode(xPathExpression);

    int quantity = Int32.Parse(inventoryItem.GetAttribute("quantity"));
    quantity -= quantitySold;
    inventoryItemElement.SetAttribute("quantity", quantity.ToString( ));
  }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.195.111