April 27, 2011, 9:47 a.m.
posted by factorial
Chapters 5 and 6 should have given you a pretty good understanding of dealing with XML tree representations. So when I say that JDOM also provides a tree-based representation of an XML document, that gives you a starting point for understanding how JDOM behaves. To help you see how the classes in JDOM match up to XML structures, take a look at Figure, which shows a UML model of JDOM's core classes.
UML model of core JDOM classes
As you can see, the names of the classes tell the story. At the core of the JDOM structure is the Document object, which is both the representation of an XML document, and a container for all the other JDOM structures. Element represents an XML element, Attribute an attribute, Text and CDATA represent character data within Element objects, and so on down the line.
Java Collections Support
Another important item to take note of is that you don't see any list classes like SAX's Attributes class or DOM's NodeList and NamedNodeMap classes. This is a nod to Java developers; it was decided that using Java collections (java.util.List, java.util.Map, etc.) would provide a familiar and simple API for XML usage. DOM must serve across languages (remember Java language bindings in Chapter 5?), and can't take advantage of language-specific things like Java collections. For example, when invoking the getAttributes( ) method on the Element class, you get back a List; you can of course operate upon this List just as you would any other Java List, without looking up new methods or syntax. The List objects returned by JDOM are "live" so that a call such as element.getAttributes.clear( ) will remove all the attributes from the element object.
Concrete Classes and Factories
Another basic tenet of JDOM that is different from DOM, and not as visible, is that JDOM is an API of concrete classes. In other words, Element, Attribute, ProcessingInstruction, Comment, and the rest are all classes that can be directly instantiated using the new keyword. This generally makes JDOM document construction code much simpler than the corresponding DOM code, since you don't need to create a DocumentBuilderFactory or a DocumentBuilder. Creating a new JDOM document is done like this:
Element rootElement = new Element("root"); Document document = new Document(rootElement);
element.addContent(new FooterElement("Copyright 2006"));
Here, FooterElement is a subclass of org.jdom.Element and does some custom processing (it could, for example, build up several elements that display a page footer). Because it subclasses Element, it can be added to the element variable through the normal means: the addContent( ) method. But since these objects are instantiated with the new keyword, there's no way to instruct JDOM to create FooterElement instances for all elements. For these cases, JDOM does have a factory interface, org.jdom.JDOMFactory , which allows you to specify which subclass should be used. We'll look at JDOMFactory in more detail in the "JDOM and Factories" section later in this chapter.
Useful Return Values
Unlike standard JavaBeans, the setter methods of various JDOM classes return the object on which the setter was called. This allows you to write code that creates a whole XML document with one line of Java code, such as:
Document doc = new Document(new Element("root").setAttribute("attribute", "value").addContent(new Element("inner")).addContent( new Comment("comment text")).addContent("some inline text") .addContent(new Element("inner2")));
To produce a document like:
<?xml version="1.0" encoding="UTF-8"?> <root attribute="value"> <inner /> <!--comment text--> some inline text <inner2 /> </root>
Of course, this should be used judiciously, as the produced code can be so complex as to be unreadable. But you will frequently see cases where an Element is created, given some character data as content, and added to a parent element with one line of code:
Input and Output
A final important aspect of JDOM is its input and output model. First, it is important to understand that JDOM is not a parser; it is an XML document representation in Java. In other words, like DOM and SAX, it is simply a set of classes that can be used to manipulate the data that a parser provides. As a result, JDOM must rely on a parser for reading raw XML. (By default, this is the parser configured through JAXP. See Chapter 7 for more details on JAXP.) It can also accept SAX events or a DOM tree as input, as well as JDBC ResultSet instances and more. To facilitate this, JDOM provides a package specifically for input, org.jdom.input. This package provides builder classes; the two you'll use most often are SAXBuilder and DOMBuilder. These build the core JDOM structure, a JDOM Document, from a set of SAX events or a DOM tree. For dealing with input streams, files or documents on disk, or building from existing XML not in a DOM tree, SAXBuilder is the best solution. It's fast and efficient, just like SAX. Using the builder is a piece of cake:
SAXBuilder builder = new SAXBuilder( ); Document doc = builder.build(new FileInputStream("contents.xml"));
I will detail this further in the code in the chapter, but you can see that it doesn't take much to get access to XML. If you already have your document in a DOM structure, you'll want to use DOMBuilder, which performs a fast conversion from one API to the other:
DOMBuilder builder = new DOMBuilder( ); Document doc = builder.build(myDomDocumentObject);
It is fairly self-explanatory. This essentially converts from an org.w3c.dom.Document to an org.jdom.Document. The process of converting from a JDOM document back to one of these structures is essentially the same, in reverse; the org.jdom.output package is used for these tasks. To move from JDOM structures to DOM ones, DOMOutputter is used:
DOMOutputter outputter = new DOMOutputter( ); org.w3c.dom.Document domDoc = outputter.output(myJDOMDocumentObject);
Taking a JDOM Document and firing off SAX events works in the same way:
SAXOutputter outputter = new SAXOutputter( ); outputter.setContentHandler(myContentHandler); outputter.setErrorHandler(myErrorHandler); outputter.output(myJDOMDocumentObject);
This works just like dealing with normal SAX events, where you register content handlers, error handlers, and the rest, and then fire events to those handlers from the JDOM Document object supplied to the output( ) method.
The final outputter, and the one you'll probably work with more than any other, is org.jdom.output.XMLOutputter . This outputs XML to a stream or writer, which wraps a network connection, a file, or any other structure you want to push XML to. This is also effectively a production-ready version of the DOMSerializer class from Chapter 5, except of course it works with JDOM, not DOM. Using the XMLOutputter works like this:
XMLOutputter outputter = new XMLOutputter( ); outputter.output(jdomDocumentObject, new FileOutputStream("results.xml"));
The format of the output from XMLOutputter is highly configurable through the Format class, as we'll see later in this chapter. By default, XMLOutputter will output without any added newlines or indentation.
So there you have it: the input and output of JDOM all in a few paragraphs. One last thing to note, as illustrated in Figure: it is very easy to "loop" things because all the input and output of JDOM is actually part of the API. In other words, you can use a file as input, work with it in JDOM, output it to SAX, DOM, or a file, and then consume that as input, restarting the loop. This is particularly helpful in messaging-based applications or in cases where JDOM is used as a component between other XML supplying and consuming components.
Input and output loops in JDOM
The JDOM Distribution
The JDOM 1.0 distribution, downloadable from http://www.jdom.org, contains the JDOM JAR filejdom.jaras well as seven additional JAR files. One of theseant.jaris used only by the JDOM build process. The rest are JDOM's compile and runtime dependencies. Because JDOM is compatible with all Java versions since Java 1.2, the distribution includes several JAR files that are not required using later versions of the Java runtime. Specifically, if you are using a version of Java after, and including, Java 1.4, the following JAR files are not needed: xalan.jar, xerces.jar, and xml-apis.jar. In addition, the JAR files jaxen-core.jar, jaxen-jdom.jar, and saxpath.jar are needed only if you use JDOM's XPath functionality.
This is not a comprehensive look at JDOM, but it gives you enough information to get started; and I'd rather show you things within the context of working code anyway! So, let's take a look at a utility program that can convert Java properties files to XML.