home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  

Book HomeJava and XML, 2nd EditionSearch this book

6.2. Namespaces

An important addition to DOM Level 2 not yet discussed is DOM's support for XML namespaces. You'll remember from Chapter 3, "SAX" and Chapter 4, "Advanced SAX ", that SAX 2.0 added namespace support, and the same is true for the second iteration of DOM. The key here is two new 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( ).


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 backwards compatiblity 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 straight 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.oreilly.com" and a prefix of "ora" on an element called "copyright", you would call createElementNS("http://www.oreilly.com", "ora:copyright"). 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 correctly return "ora", though, as it should. If you want the element in the default namespace (with no prefix), just pass in the element name (the local name, in this case), 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.

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. I'm hoping the next version of the specification modifies this to return an empty string ("") when the element is in the default namespace.

Rather than simply list all the new namespace-aware methods (you can find that list in Appendix A, "API Reference"), I'd rather show you some real code. In fact, converting the UpdateItemServlet's doPost( ) method to use namespaces is a perfect example:

    public void doPost(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        // Get parameter values
        String id = req.getParameterValues("id")[0];
        String name = req.getParameterValues("name")[0];
        String description = req.getParameterValues("description")[0];

        // See if this file exists
        Document doc = null;
        File xmlFile = new File(ITEMS_DIRECTORY + "item-" + id + ".xml");
        }String docNS = "http://www.oreilly.com/javaxml2";}

        if (!xmlFile.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);

            // Description of item
            Element descriptionElement = 
                doc.createElementNS(docNS, "description");
            Text descriptionText = doc.createText(description);
  } else {
            // Load document
            try {
                DOMParser parser = new DOMParser( );
                parser.parse(xmlFile.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( );
                // Description of item
                NodeList descriptionElements = 
                    root.getElementsByTagNameNS(docNS, "description");
                Element descriptionElement = (Element)descriptionElements.item(0);

                // Remove and recreate description
                descriptionElement = doc.createElementNS(docNS, "description");
                Text descriptionText = doc.createTextNode(description);
            } catch (SAXException e) {
                // Print error
                PrintWriter out = res.getWriter( );
                out.println("<HTML><BODY>Error in reading XML: " +
                    e.getMessage( ) + ".</BODY></HTML>");
                out.close( ); 

        // Serialize DOM tree
        DOMSerializer serializer = new DOMSerializer( );
        serializer.serialize(doc, xmlFile);

        // Print confirmation
        PrintWriter out = res.getWriter( );
        out.println("<HTML><BODY>Thank you for your submission. " +
            "Your item has been processed.</BODY></HTML>");
        out.close( );        

Using the createElementNS( ) method to create namespaced elements and searching for them with getElementsByTagNameNS( ) seems to be perfect. 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"?>
<item id="bourgOM">
<name>Bourgeois OM Guitar</name>
<description>This is a <i>beautiful</i> <b>Sitka-topped</b> guitar with 
<b>Indian Rosewood</b> back and sides. Made by luthier 
<a href="http://www.bourgeoisguitars.com">Dana Bourgeois</a>, this OM has a 
<b>huge sound</b>. 
The guitar has <i>great action</i>, a 1 3/4" nut, and all 
<i>fossilized ivory</i> nut and saddle, with <i>ebony</i> end pins.
New condition, this is a <b>great guitar</b>!</description>

Does this look familiar? It is the XML from earlier, with no change! 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; changes are made just as they were before. However, your documents should now have namespaces, both in the reading and writing portion of the servlet application.

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.

Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.