站内搜索: 请输入搜索关键词
当前页面: 图书首页 > XML and Java: Developing Web Applications, Second Edition

XML and Java: Developing Web Applications, Second Edition

[ directory ] Previous Section Next Section

3.2 Creating a DOM Tree from Scratch

This section covers how to create DOM objects and how to construct a tree by using example programs.

3.2.1 Creating a Document Object

First, we show how to create a DOM tree without reading an XML document externally. The steps for the creation are as follows:

  1. Create an org.w3c.dom.Document object.

  2. Create a root element (document element).

  3. Append the root element as a child of the Document object.

  4. Create elements (with attributes), text, and comment nodes, and construct a DOM tree.

If you want to see a complete program in advance, refer to Listing 3.1. Recall that in the DOM tree structure, a document is represented by a Document object, so this is what you need to create first. There are several ways to create the object:

  • Create an object of the implementation class for the Document interface directly.

  • Get an object of the implementation class for the Document interface by using Java's reflection capability.

  • Get an object of the implementation class for the Document interface by using the JAXP API.

The implementation class for the Document interface is org.apache.xerces.dom.DocumentImpl. In the first approach, the class is explicitly generated as follows:

import org.w3c.dom.Document;
import org.apache.xerces.dom.DocumentImpl;
...
// Creates Document object
Document doc = new DocumentImpl();
...

This program fragment is very simple, but you must modify the code if you want to use an XML processor other than Xerces, because the implementation class of the Document interface depends on the implementation of the XML processor.

If the DOM implementation is to be dynamically selected at runtime, you can use Java's reflection feature to create a class object and then create an instance of it as follows:

import org.w3c.dom.Document;
...
// Creates Document object
String documentImpl = "org.apache.xerces.dom.DocumentImpl";
Document doc = (Document)Class.forName(documentImpl).newInstance();
...

Note that the org.apache.xerces.dom.DocumentImpl class is not imported in the previous example. The class name can be specified by using a command-line argument, a system property, or an external file at runtime. The second approach is a little more complicated but preferable because it can compile without Xerces and is more XML processor杋ndependent.

From the viewpoint of XML processor dependence, using the JAXP API is the best way to create a Document object. As described in Chapter 2, the javax.xml.parsers.DocumentBuilderFactory class is used as a DOM parser. This class provides the newDocument() method, which returns a Document object. This method makes it possible to get a Document object without depending on specific implementation classes. An example follows.

import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
...
// Creates Document object
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
...

3.2.2 Creating and Appending Child Nodes

Other DOM objects, such as Element and Text, also need to be created as implementation-specific objects. However, the Document interface provides implementation-neutral methods for creating them. Therefore, after you get the Document object, you can create these nodes without specifying their implementation classes. This is based on the design patterns mentioned in Section 2.3.4.

Let's look at how we use the patterns. Suppose that we want to create a person element and set it as the root element doc. In Xerces, the Element interface is implemented by the class org.apache.xerces.dom.ElementImpl, so some simple code may look like this:

Element root = new ElementImpl((DocumentImpl)doc, "person");
doc.appendChild(root);

Unfortunately, this code is dependent on the specific implementation; Xerces. We can eliminate this kind of implementation dependency by using a factory method. Therefore the previous example can be rewritten as:

Element root = doc.createElement("person");

In this code, the Element node is not created directly by using a new function but is created with the createElement() method. Now the code is solely written in the DOM API and so is more portable. Listing 3.1 lists the factory methods provided by the Document interface. In the DOM Level 2 specification, two methods were added to support namespaces.

Listing 3.1 Factory methods provided by the Document interface
public Element createElement(String tagName)
public Element createElementNS(String namespaceURI,
String qualifiedName) (Level 2)
public DocumentFragment createDocumentFragment()
public Text createTextNode(String data)
public Comment createComment(String data)
public CDATASection createCDATASection(String data)
public ProcessingInstruction createProcessingInstruction(String
target, String data)
public Attr createAttribute(String name)
public Attr createAttributeNS(String namespaceURI, String
qualifiedName) (Level 2)
public EntityReference createEntityReference(String name)

The following objects can be attached to a Document object as its children (remember that Document is also derived from Node):

  • At most, one DocumentType object梩his represents the DTD

  • One Element object梩his is the root element

  • Zero or more Comment objects

  • Zero or more ProcessingInstruction objects

We have already created the root element. Next, we add a new element named name as a child node of the root element (the person element just created).

Element item = doc.createElement("name");
item.appendChild(doc.createTextNode("John Doe"));
root.appendChild(item);

We created a name element that contains a Text node with the content string "John Doe" and appended the element as the (last) child of the root element.

An Element object can take the following objects as children:

  • Any number of Element objects

  • Any number of EntityReference objects

  • Any number of Text objects

  • Any number of CDATASection objects

  • Any number of Comment objects

  • Any number of ProcessingInstruction objects

At this point, we have created the DOM object tree shown in Figure 3.1.

Figure 3.1. A generated DOM tree

graphics/03fig01.gif

Note that you can recursively add elements to build a complex tree structure.

Listing 3.2 shows a complete program that generates and outputs a simple XML document. This code creates a Document instance (lines 16?0), creates and appends the root element (line 23), and creates a comment (lines 25?6), an element and an attribute (lines 29?1), and a processing instruction (lines 32?4). The created DOM tree is output by using the serialization package from Xerces (lines 48?3). It is important to output a well-formed or valid XML document to be able to store it or pass it to other applications. We discuss serialization in Section 3.4.

Listing 3.2 Creating a DOM tree, chap03/MakeDocumentWithFactory.java
       package chap03;
       /**
        *       MakeDocumentWithFactory.java
        **/
       import org.w3c.dom.Document;
       import org.w3c.dom.Element;
       import org.w3c.dom.Text;
       import org.w3c.dom.ProcessingInstruction;
[9]    import org.apache.xml.serialize.XMLSerializer;
[10]   import org.apache.xml.serialize.OutputFormat;

       public class MakeDocumentWithFactory {
          public static void main(String[] argv) {
             try {
[15]             // Creates Document object
[16]             String documentImpl =
[17]                "org.apache.xerces.dom.DocumentImpl";
[18]             Document doc = (Document)Class.
[19]                forName(documentImpl).newInstance();
[20]             // Creates <department> element as root
[21]             Element root = doc.createElement("department");
[22]             // Sets the element as root
[23]             doc.appendChild(root);
[24]             // Creates comment node
[25]             root.appendChild(doc.createComment(
[26]                "The first employee description."));
[27]             // Creates <employee> elements and its text
[28]             // content, and adds it
[29]             Element employee = doc.createElement("employee");
[30]             employee.setAttribute("id", "J.D");
[31]             root.appendChild(employee);
[32]             // Create Processing Instruction
[33]             root.appendChild(doc.createProcessingInstruction(
[34]                                 "application", "commandForApp"));
                 // Creates <name> element and adds it
                 Element name = doc.createElement("name");
                 name.appendChild(doc.createTextNode("John Doe"));
                 employee.appendChild(name);
                 // Creates <email> element and adds it
                 Element email = doc.createElement("email");
                 email.appendChild(
                    doc.createTextNode("John.Doe@foo.com"));
                 employee.appendChild(email);

                 // Prepares output format
                 OutputFormat formatter = new OutputFormat();
[47]             // Preserves whitespace
[48]             formatter.setPreserveSpace(true);
[49]             // The XML document is output to standard output
[50]             XMLSerializer serializer =
[51]                 new XMLSerializer(System.out, formatter);
[52]             // Serializes the DOM tree as an XML document
[53]             serializer.serialize(doc);

            } catch (Exception e) {
                 e.printStackTrace();
            }
        }
    }

Executing this program produces the following output.

R:\samples>java chap03.MakeDocumentWithFactory
<?xml version="1.0" encoding="UTF-8"?>
<department><!--The first employee description.--><employee id="J.D">
<name>
John Doe</name><email>John.Doe@foo.com</email></employee>
<?application commandForApp?></department>

Although the result is divided into multiple lines, it is actually output on one line (compare it with department.xml, which appeared in Chapter 2. This occurs because no whitespace for improving readability is contained in the program-generated XML document. Handling whitespace is discussed further in Section 3.5.

Listing 3.3 shows a JAXP version (MakeDocumentWithFactoryJAXP) to generate the same XML document. The Document object can be created by using the DocumentBuilder class. That means the class is a factory to create new Document objects, and newDocumentBuilder() is a factory method.

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();

Listing 3.3 contains no implementation class. If you want to use Listing 3.2 with an XML processor other than Xerces, the program should be modified (line 17) because the DOM specification does not provide an API to create a Document instance. When you use Listing 3.3, you can use it with other JAXP-aware XML processors without modification.

Listing 3.3 Creating a DOM tree with the JAXP API, chap03/ MakeDocumentWithFactoryJAXP.java
       package chap03;
       /**
        *       MakeDocumentWithFactoryJAXP.java
        **/
       import javax.xml.parsers.DocumentBuilderFactory;
       import javax.xml.parsers.DocumentBuilder;
       import org.w3c.dom.Document;
       import org.w3c.dom.Element;
       import org.w3c.dom.Text;
       import org.w3c.dom.ProcessingInstruction;
       import org.apache.xml.serialize.OutputFormat;
       import org.apache.xml.serialize.XMLSerializer;

       public class MakeDocumentWithFactoryJAXP {
          public static void main(String[] argv) {
             try {
                 // Creates document builder factory
                 DocumentBuilderFactory
                    factory = DocumentBuilderFactory.newInstance();
                 DocumentBuilder
                    builder = factory.newDocumentBuilder();
                 Document doc = builder.newDocument();

                 // Creates <department> element as root
                 Element root = doc.createElement("department");
                 // Sets the element as root
                 doc.appendChild(root);
                 // Creates comment node
                 root.appendChild(doc.createComment(
                    "The first employee description."));
                 // Creates <employee> elements and its text content,
                 // and adds it
                 Element employee = doc.createElement("employee");
                 employee.setAttribute("id", "J.D");
                 root.appendChild(employee);
                 // Create Processing Instruction
                 root.appendChild(doc.createProcessingInstruction(
                    "application", "commandForApp"));
                 // Creates <name> element and adds it
                 Element name = doc.createElement("name");
                 name.appendChild(doc.createTextNode("John Doe"));
                 employee.appendChild(name);
                 // Creates <email> element and adds it
                 Element email = doc.createElement("email");
                 email.appendChild(doc.createTextNode(
                    "John.Doe@foo.com"));
                 employee.appendChild(email);

                 // Prepares output format
                 OutputFormat formatter = new OutputFormat();
                 // Preserves whitespace
                 formatter.setPreserveSpace(true);
                 // The XML document is output to standard output
                 XMLSerializer serializer =
[55]                new XMLSerializer(System.out, formatter);
                 // Serializes the DOM tree as an XML document
                 serializer.serialize(doc);

             } catch (Exception e) {
                 e.printStackTrace();
             }
         }
     }

3.2.3 Handling Namespaces

The DOM Level 2 specification introduced two new methods (see the following) to the Element and Attr nodes for supporting namespaces. Using these methods makes it possible to create XML documents with namespaces.

public Element createElementNS(String namespaceURI, String
qualifiedName) throws DOMException
public  Attr    createAttributeNS(String namespaceURI, String
qualifiedName) throws DOMException

Both methods take two arguments. The first argument is a namespace URI. A "qualified" name is specified as the second argument. The format of a qualified name is "prefix:local-name," where "prefix" is the namespace prefix used in an XML document and "local-name" is the name of an element or attribute.

A special attribute named xmlns is used to associate a namespace URI and a prefix. This attribute is not created automatically when the methods are executed. Developers should create this attribute and set it to an appropriate element. Listing 4.1 in Section 4.3.5 shows a sample program that checks namespaces in an XML document and adds xmlns attributes automatically.

Listing 3.4 shows a sample program to create an XML document with namespaces.

Listing 3.4 Creating a DOM tree with namespaces, chap03/MakeDocumentWithNS.java
package chap03;
/**
 *       MakeDocumentWithNS.java
 **/
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import org.w3c.dom.ProcessingInstruction;
import org.apache.xml.serialize.XMLSerializer;
import org.apache.xml.serialize.OutputFormat;

public class MakeDocumentWithNS {
   public static void main(String[] argv) {
       final String NAMESPACE_URI
          = "http://www.foo.com/department";
       final String NAMESPACE_PREFIX_WITH_COLON = "ns:";
       final String NAMESPACE_SPEC
          = "http://www.w3.org/2000/xmlns/";
       try {
           // Creates document builder factory
           DocumentBuilderFactory
               factory = DocumentBuilderFactory.newInstance();
           DocumentBuilder
               builder = factory.newDocumentBuilder();
           Document doc = builder.newDocument();

           // Creates <department> element as root with namespace
           Element root = doc.createElementNS(NAMESPACE_URI,
              NAMESPACE_PREFIX_WITH_COLON+"department");
           // Sets xmlns attribute to specify namespace
           // prefix and uri
           root.setAttributeNS(NAMESPACE_SPEC, "xmlns:ns",
              NAMESPACE_URI);

           // Sets the element as root
           doc.appendChild(root);

           // Creates comment node
           root.appendChild(doc.createComment(
              "The first employee description."));

           // Creates <employee> elements and its text content,
           // and adds it
           Element employee = doc.createElementNS(NAMESPACE_URI,
              NAMESPACE_PREFIX_WITH_COLON+"employee");
           employee.setAttributeNS(NAMESPACE_URI,
              NAMESPACE_PREFIX_WITH_COLON+"id", "J.D");
           root.appendChild(employee);

           // Create Processing Instruction
           root.appendChild(doc.createProcessingInstruction(
              "application", "commandForApp"));

           // Creates <name> element and adds it
           Element name = doc.createElementNS(NAMESPACE_URI,
              NAMESPACE_PREFIX_WITH_COLON+"name");
           name.appendChild(doc.createTextNode("John Doe"));
           employee.appendChild(name);

           // Creates <email> element and adds it
           Element email = doc.createElementNS(NAMESPACE_URI,
              NAMESPACE_PREFIX_WITH_COLON+"email");
           email.appendChild(
              doc.createTextNode("John.Doe@foo.com"));
           employee.appendChild(email);

           // Prepares output format
           OutputFormat formatter = new OutputFormat();
           formatter.setPreserveSpace(true);
           // The XML document is output to standard output
           XMLSerializer serializer =
              new XMLSerializer(System.out, formatter);
           // Serializes the DOM tree as an XML document
           serializer.serialize(doc);

        } catch (Exception e) {
           e.printStackTrace();
        }
    }
 }

Executing this program produces the following output.

R:\samples>java chap03.MakeDocumentWithNS
<?xml version="1.0" encoding="UTF-8"?>
<ns:department xmlns:ns="http://www.foo.com/department"><!--The first
employee description.--><ns:employee ns:id="J.D"><ns:name>John Doe</
ns:name><ns:email>John.Doe@foo.com</ns:email></ns:employee>
<?application commandForApp?></ns:department>
    [ directory ] Previous Section Next Section