JDOM

A large portion of DOM’s complexity and unintuitiveness is a result of it not actually being a Java API. DOM is defined in the Interface Definition Language (IDL) from the Object Management Group (OMG) and then compiled to Java. This means that DOM must use interfaces and factory methods instead of classes and constructors. DOM classes must always cater to the lowest common denominator of programming languages. For example, there are no overloaded methods in DOM because JavaScript doesn’t support method overloading. That a lot of the DOM could be more concisely expressed in Java and C++ with overloaded methods doesn’t matter.

JDOM is a more intuitive, tree-based API designed just for Java. It makes no compromises to support other languages. It includes Java specific features like equals() and hashCode() methods. Where appropriate it makes free use of standard Java classes like List and String. Consequently JDOM feels a lot more intuitive to most Java programmers than DOM does. Take a look at Example 5.7. I think you’ll agree that it’s a lot easier to comprehend at first glance than the DOM equivalent.

Example 5.7. A JDOM based client for the Fibonacci XML-RPC server

import java.net.*;
import java.io.*;
import org.jdom.*;
import org.jdom.output.XMLOutputter;
import org.jdom.input.SAXBuilder;


public class FibonacciJDOMClient {

  public final static String DEFAULT_SERVER 
   = "http://www.elharo.com/fibonacci/XML-RPC";  
  
  public static void main(String[] args) {
      
    if (args.length <= 0) {
      System.out.println(
       "Usage: java FibonacciJDOMClient number url"
      );
      return;
    }
    
    String server = DEFAULT_SERVER;
    if (args.length >= 2) server = args[1];
      
    try {
      // Build the request document
      Element methodCall = new Element("methodCall");
      Element methodName = new Element("methodName");
      methodName.setText("calculateFibonacci");
      methodCall.addContent(methodName);
      Element params = new Element("params");
      methodCall.addContent(params);
      Element param = new Element("param");
      params.addContent(param);
      Element value = new Element("value");
      param.addContent(value);
      // Had to break the naming convention here because of a 
      // conflict with the Java keyword int
      Element intElement = new Element("int");
      intElement.setText(args[0]);
      value.addContent(intElement);  
      Document request = new Document(methodCall);
      
      // Transmit the request document
      URL u = new URL(server);
      URLConnection uc = u.openConnection();
      HttpURLConnection connection = (HttpURLConnection) uc;
      connection.setDoOutput(true);
      connection.setDoInput(true); 
      connection.setRequestMethod("POST");
      OutputStream out = connection.getOutputStream();
      
      XMLOutputter serializer = new XMLOutputter();
      serializer.output(request, out);
      
      out.flush();
      out.close();        

      // Read the response
      InputStream in = connection.getInputStream();
      SAXBuilder parser = new SAXBuilder();
      Document response = parser.build(in);
      in.close();
      connection.disconnect();
      
      // Walk down the tree
      String result = response.getRootElement()
                       .getChild("params")
                       .getChild("param")
                       .getChild("value")
                       .getChild("double")
                       .getText();
      System.out.println(result);
    }
    catch (Exception e) {
      System.err.println(e); 
    }
  
  } 

}

In JDOM the request document is built as a tree. JDOM, however, uses classes and constructors rather than factory methods. Elements can be created that are not attached to any document. Elements and most other nodes are added to a document by passing them to their parent’s addContent() method. However, if an element only contains text, no markup, then its entire content may be set by passing a string to its setText() method instead.

To serialize documents JDOM provides the XMLOutputter class. This class combines both formatting options such as encoding and the actual writing of the document onto a stream or writer. This is a standard part of JDOM that is used the same way no matter which parser you use underneath JDOM.

Once the response has been received, JDOM parses it into a Document object using the SAXBuilder class. This is the class that connects JDOM to an underlying parser that implements the SAX API. JDOM does not provide convenient methods for extracting subsets of the nodes in a document as DOM does. Consequently to get to the element we want, we must walk the tree. Since the document is very structured, this is straightforward. First get the root element of the document, then get its params child, then its param child, then its value child, then its double child, and finally its text content. The result is the string value we need. Although this string value happens to represent a double, JDOM has no way of knowing this (nor does any other XML API).

JDOM is definitely easier than DOM for simple problems like this. However, I think it fails when it comes to the more complex problems such as processing large narrative documents with lots of mixed content or working with XPath based technologies like XSLT, XPointers, XInclude, and XQuery. The design of the JDOM API has often chosen simplicity and intuitiveness over power and completeness. The incomplete tree model really begins to hurt as the problems grow more complex. Any operation that requires you to recursively walk the entire document tree is more complex in JDOM than in DOM. JDOM is much better suited to data oriented documents with well-known record-like structures that do not contain any mixed content.


Copyright 2001, 2002 Elliotte Rusty Haroldelharo@metalab.unc.eduLast Modified September 09, 2002
Up To Cafe con Leche