Book HomeJava and XML, 2nd Edition

7.5. Gotcha!

Not to disappoint, I want to warn you of some common JDOM pitfalls. I hope this will save you a little time in your JDOM programming.

7.5.1. JDOM isn't DOM

First and foremost, you should realize that JDOM isn't DOM. It doesn't wrap DOM, and doesn't provide extensions to DOM. In other words, the two have no technical relation to each other. Realizing this basic truth will save you a lot of time and effort; there are many articles out there today that talk about getting the DOM interfaces to use JDOM, or avoiding JDOM because it hides some of DOM's methods. These statements confuse more people than almost anything else. You don't need to have the DOM interfaces, and DOM calls (like appendChild( ) or createDocument( )) simply won't work on JDOM. Sorry, wrong API!

7.5.2. Null Return Values

Another interesting facet of JDOM, and one that has raised some controversy, is the return values from methods that retrieve element content. For example, the various getChild( ) methods on the Element class may return a null value. I mentioned this, and demonstrated it, in the PropsToXML example code. The gotcha occurs when instead of checking if an element exists (as was the case in the example code), you assume that an element already exists. This is most common when some other application or component sends you XML, and your code expects it to conform to a certain format (be it a DTD, XML Schema, or simply an agreed-upon standard). For example, take a look at the following code:

Document doc = otherComponent.getDocument( );
String price = doc.getRootElement( ).getChild("item")
                                   .getChild("price")
                                   .getTextTrim( );

The problem in this code is that if there is no item element under the root, or no price element under that, a null value is returned from the getChild( ) method invocations. Suddenly, this innocuous-looking code begins to emit NullPointerExceptions, which are quite painful to track down. You can handle this situation in one of two ways. The first is to check for null values at each step of the way:

Document doc = otherComponent.getDocument( );
Element root = doc.getRootElement( );
Element item = root.getChild("item");
if (item != null) {
    Element price = item.getChild("price");
    if (price != null) {
        String price = price.getTextTrim( );
    } else {
        // Handle exceptional condition
    }
} else {
    // Handle exceptional condition
}

The second option is to wrap the entire code fragment in a try/catch block:

Document doc = otherComponent.getDocument( );
try {
    String price = doc.getRootElement( ).getChild("item")
                                       .getChild("price")
                                       .getTextTrim( );
} catch (NullPointerException e) {
    // Handle exceptional condition
}

While either approach works, I recommend the first; it allows finer-grained error handling, as it is possible to determine exactly which test failed, and therefore exactly what problem occurred. The second code fragment informs you only that somewhere a problem occurred. In any case, careful testing of return values can save you some rather annoying NullPointerExceptions.

7.5.3. DOMBuilder

Last but not least, you should be very careful when working with the DOMBuilder class. It's not how you use the class, but when you use it. As I mentioned, this class works for input in a similar fashion to SAXBuilder. And like its SAX sister class, it has build( ) methods that take in input forms like a Java File or InputStream. However, building a JDOM Document from a file, URL, or I/O stream is always slower than using SAXBuilder; that's because SAX is used to build a DOM tree in DOMBuilder, and then that DOM tree is converted to JDOM. Of course, this is much slower than leaving out the intermediary step (creating a DOM tree), and simply going straight from SAX to JDOM.

So, any time you see code like this:

DOMBuilder builder = new DOMBuilder( );

// Building from a file
Document doc = builder.build(new File("input.xml"));

// Building from a URL
Document doc = builder.build(
    new URL("http://newInstance.com/javaxml2/copyright.xml"));

// Building from an I/O stream
Document doc = builder.build(new FileInputStream("input.xml"));

You should run screaming! Seriously, DOMBuilder has its place: it's great for taking existing DOM structures and going to JDOM. But for raw, speedy input, it's simply an inferior choice in terms of performance. Save yourself some headaches and commit this fact to memory now!



Library Navigation Links

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