Home > Articles > Web Services > XML

  • Print
  • + Share This
This chapter is from the book

JDOM

A large portion of DOM's complexity and unintuitiveness derives from the fact that it's not actually 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. It doesn't matter that in Java and C++ a lot of the DOM could be expressed more concisely with overloaded methods.

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 such as equals() and hashCode() methods. Where appropriate it makes free use of standard Java classes such as 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. Rather than factory methods, however, JDOM uses classes and constructors. Furthermore, it's possible to create elements that are not attached to any document. Elements and most other nodes are added to a document by their parent's addContent() method. However, if an element contains only text and 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 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 used underneath JDOM.

Once it has received the response, 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. Because the document is very structured, this is straightforward. We get first the root element of the document, then 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. But I think it fails when it comes to more complex problems such as processing large narrative documents with lots of mixed content, or working with XPath-based technologies like XSLT, XML Pointer Language (XPointer), XInclude, and XQuery. JDOM often chooses 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 walk the entire document tree recursively is more complex in JDOM than it is in DOM. JDOM is much better suited to data-oriented documents with well-known record-like structures that do not contain any mixed content.

  • + Share This
  • 🔖 Save To Your Account