So far, I've basically punted on the issue of XML namespaces. Happily, DOM does support namespaces, so let's get into that now. This support is achieved through two methods on the Node interface: getPrefix( ) and getNamespaceURI( ). Additionally, all of the creation methods have namespace-aware versions available. So, instead of calling createElement( ), you call createElementNS( ).

Overloaded?

To all of the Java programmers out there, createElementNS( ) looks pretty odd. Why not just overload createElement( ) to take in additional parameters? Well, you could if DOM was used only in Java or in languages that supported overloading. However, it's not; it's a cross-language specification, and that results in limitations with method names and backward compatibility when it comes to changing existing method signatures. So, DOM defined new methods with the NS suffix to identify them as namespace-aware versions. It's bad for Java but good for DOM as a cross-language standard.

In each of these new namespace-aware methods, the first argument is the namespace URI, and the second is the qualified name of the element, attribute, etc. Note that I said qualified; this means that if you want to use a namespace URI of http://www.ajaxian.com and a prefix of ajax on an element called blog-entry, you would call createElementNS("http://www.ajaxian.com", "ajax:blog-entry"). This is very important, and remembering to use that prefix will save you a lot of time down the road. Calling getPrefix( ) on that new element will return "ajax". If you want the element in the default namespace (with no prefix), just pass in the element name (the local name), and you're all set. Calling getPrefix( ) on a default-namespaced element returns null, by the way, as it does on an element not in any namespace.

Java Warning The prefix tells you very little about whether an element is a namespace. Elements with a default namespace (and no prefix) have the same return value from getPrefix( ) as elements not in any namespace.

Rather than simply list all the new namespace-aware methods, let's look at some real code. Here's the bulk of the doPost( ) method from ModifyServlet, using namespaces:

// See if this file exists Document doc = null;
String filename = outputDir + "item-" + id + ".xml";
File outputFile = new File(filename);
String docNS = "http://www.guitarnotes.com";
if (!outputFile.exists( )) {
 // Create new DOM tree
 DOMImplementation domImpl = new DOMImplementationImpl( );
 doc = domImpl.createDocument(docNS, "item", null);
 Element root = doc.getDocumentElement( );
 // ID of item (as attribute)
 root.setAttribute("id", id);
 // Name of item
 Element nameElement = doc.createElementNS(docNS, "name");
 Text nameText = doc.createTextNode(name);
 nameElement.appendChild(nameText);
 root.appendChild(nameElement);
 // Description of item
 Element descriptionElement = doc.createElementNS(docNS, "description");
 Text descriptionText = doc.createTextNode(description);
 descriptionElement.appendChild(descriptionText);
 root.appendChild(descriptionElement);
} else {
 // Load document
 try {
 DOMParser parser = new DOMParser( );
 parser.parse(outputFile.toURL( ).toString( ));
 doc = parser.getDocument( );
 Element root = doc.getDocumentElement( );
 // Name of item
 NodeList nameElements = root.getElementsByTagNameNS(docNS, "name");
 Element nameElement = (Element)nameElements.item(0);
 Text nameText = (Text)nameElement.getFirstChild( );
 nameText.setData(name);
 // Description of item
 NodeList descriptionElements = root.getElementsByTagNameNS(docNS, "description");
 Element descriptionElement = (Element)descriptionElements.item(0);
 // Remove and recreate description
 root.removeChild(descriptionElement);
 descriptionElement = doc.createElementNS(docNS, "description");
 Text descriptionText = doc.createTextNode(description);
 descriptionElement.appendChild(descriptionText);
 root.appendChild(descriptionElement);
 } catch (SAXException e) {
 // Error handling
 }
}

Using the createElementNS( ) method to create namespaced elements and searching for them with getElementsByTagNameNS( ) is all that's needed to move to namespace-aware code. The createDocument( ) method even has a handy place to insert the namespace URI for the root element. These elements are all put into the default namespace, and everything looks fine. However, there is a big problem here; look at the output from running this servlet with no existing XML (this is generated XML, rather than modified XML):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<item >
<name>Ryan Pierre Bensusan Signature Model</name>
<description>This amazing Kevin Ryan guitar is a collaboration between Kevin and &lt;a href="http://web.archive.org/web/www.pierrebensusan.com"&gt;Pierre Bensusan&lt;/a&gt;. The guitar has a &lt;b&gt;cedar&lt;/b&gt; top and either rosewood (in Indian, or &lt;b&gt;Brazilian&lt;/b&gt;!) or Mahogany back and sides. For more, check out the &lt;a href="http://web.archive.org/web/www.ryanguitars.com/theguitars/signature/pbs/pbsmain.htm"&gt;Pierre Bensusan page&lt;/a&gt; at &lt;a href="http://web.archive.org/web/www.ryanguitars.com"&gt;Ryan Guitars&lt;/a&gt;.</description>
</item>

Do you see a problem here? There are no namespace declarations, even though the NS methods were used. The one thing that DOM does not do is add namespace declarations. Instead, you'll need to manually add the xmlns attribute to your DOM tree; otherwise, when reading in the document, the elements won't be placed into a namespace and you will have some problems. One small change takes care of this, though:

// Create new DOM tree DOMImplementation domImpl = new DOMImplementationImpl( );
doc = domImpl.createDocument(docNS, "item", null);
Element root = doc.getDocumentElement( );
root.setAttribute("xmlns", docNS);

Now you'll get the namespace declaration that you were probably expecting to show up the first go-round. You can compile these changes and try things out. You won't notice any difference in how the servlet runs; changes are made just as they were before. However, your documents should now have namespaces, both in the reading and writing portions of the servlet app. A final word on this namespace detail: keep in mind that you could certainly modify the DOMSerializer class to look for namespaces on elements and print out the appropriate xmlns declarations as it walks the tree. This is a perfectly legal change, and would be sort of valuable; in fact, it's what many solutions, like those found within Xerces, already do. In any case, as long as you are aware of this behavior, you are protected from being the victim of it.