You now know how to write Java programs that read XML. Let us now turn to the opposite process: producing XML output. Of course, you could write an XML file simply by making a sequence of print calls, printing the elements, attributes, and text content, but that would not be a good idea. The code is rather tedious, and you can easily make mistakes if you don’t pay attention to special symbols (such as ” or <) in the attribute values and text content.
A better approach is to build up a DOM tree with the contents of the document and then write out the tree contents. The following sections discuss the details.
1. Documents without Namespaces
To build a DOM tree, you start out with an empty document. You can get an empty document by calling the newDocument method of the DocumentBuilder class:
Document doc = builder.newDocument();
Use the createElement method of the Document class to construct the elements of your document:
Element rootElement = doc.createElement(rootName);
Element childElement = doc.createElement(childName);
Use the createTextNode method to construct text nodes:
Text textNode = doc.createTextNode(textContents);
Add the root element to the document, and add the child nodes to their parents:
doc.appendChild(rootElement);
rootElement.appendChild(childElement);
childElement.appendChild(textNode);
As you build up the DOM tree, you may also need to set element attributes. Simply call the setAttribute method of the Element class:
rootElement.setAttribute(name, value);
2. Documents with Namespaces
If you use namespaces, the procedure for creating a document is slightly different.
First, set the builder factory to be namespace-aware, then create the builder:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
builder = factory.newDocumentBuilder();
Then use createElementNS instead of createElement to create any nodes:
String namespace = “http://www.w3.org/2000/svg”;
Element rootElement = doc.createElementNS(namespace, “svg”);
If your node has a qualified name with a namespace prefix, then any necessary xmlns-prefixed attributes are created automatically. For example, if you need SVG inside XHTML, you can construct an element like this:
Element svgElement = doc.createElement(namespace, “svg:svg”)
When the element is written, it turns into
<svg:svg xmlns:svg=”http://www.w3.org/2000/svg”>
If you need to set element attributes whose names are in a namespace, use the setAttributeNS method of the Element class:
rootElement.setAttributeNS(namespace, qualifiedName, value);
3. Writing Documents
Somewhat curiously, it is not so easy to write a DOM tree to an output stream. The easiest approach is to use the Extensible Stylesheet Language Transformations (XSLT) API. For more information about XSLT, turn to Section 3.9, “XSL Transformations,” on p. 216. Right now, consider the code that follows a magic incantation to produce XML output.
We apply the do-nothing transformation to the document and capture its output. To include a DOCTYPE node in the output, we also need to set the SYSTEM and PUBLIC identifiers as output properties.
// construct the do-nothing transformation
Transformer t = TransformerFactory.newInstance().newTransformer();
// set output properties to get a DOCTYPE node
t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, systemIdentifier);
t.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, publicIdentifier);
// set indentation
t.setOutputProperty(OutputKeys.INDENT, “yes”);
t.setOutputProperty(OutputKeys.METHOD, “xml”);
t.setOutputProperty(“{http://xml.apache.org/xslt}indent-amount”, “2”);
// apply the do-nothing transformation and send the output to a file
t.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(file)));
Another approach is to use the LSSerializer interface. To get an instance, you have to use the following magic incantation:
DOMImplementation impl = doc.getImplementation();
var implLS = (DOMImplementationLS) impl.getFeature(“LS”, “3.0”);
LSSerializer ser = implLS.createLSSerializer();
If you want spaces and line breaks, set this flag:
ser.getDomConfig().setParameter(“format-pretty-print”, true);
Then it’s simple enough to convert a document to a string:
String str = ser.writeToString(doc);
If you want to write the output directly to a file, you need an LSOutput:
LSOutput out = implLS.createLSOutput();
out.setEncoding(“UTF-8”);
out.setByteStream(Files.newOutputStream(path));
ser.write(doc, out);
4. Writing an XML Document with StAX
In the preceding section, you saw how to produce an XML document by writing a DOM tree. If you have no other use for the DOM tree, that approach is not very efficient.
The StAX API lets you write an XML tree directly. Construct an XMLStreamWriter from an OutputStream:
XMLOutputFactory factory = XMLOutputFactory.newInstance();
XMLStreamWriter writer = factory.createXMLStreamWriter(out);
To produce the XML header, call
writer.writeStartDocument()
Then call
writer.writeStartElement(name);
Add attributes by calling
writer.writeAttribute(name, value);
Now you can add child elements by calling writeStartElement again, or write characters with
writer.writeCharacters(text);
When you have written all child nodes, call
writer.writeEndElement();
This causes the current element to be closed.
To write an element without children (such as <img . . ./>), use the call
writer.writeEmptyElement(name);
Finally, at the end of the document, call
writer.writeEndDocument();
This call closes any open elements.
You still need to close the XMLStreamWriter, and you need to do it manually since the XMLStreamWriter interface does not extend the AutoCtoseabte interface.
As with the DOM/XSLT approach, you don’t have to worry about escaping characters in attribute values and character data. However, it is possible to produce malformed XML, such as a document with multiple root nodes. Also, the current version of StAX has no support for producing indented output.
The program in Listing 3.9 shows you both approaches for writing XML.
5. An Example: Generating an SVG File
Listing 3.9 is a typical program that produces XML output. The program draws a modernist painting—a random set of colored rectangles (see Figure 3.3). To save our masterpiece, we use the Scalable Vector Graphics (SVG) format. SVG is an XML format to describe complex graphics in a device-independent fashion. You can find more information about SVG at www.w3.org/Graphics/SVG. To view SVG files, simply use any modern browser.
The program demonstrates two ways of producing XML: by constructing and saving a DOM tree, and by directly writing the XML with the StAX API.
We don’t need to go into details about SVG; for our purposes, we just need to know how to express a set of colored rectangles. Here is a sample:
<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE svg PUBLIC “-//W3C//DTD SVG 20000802//EN”
“http://www.w3.org/TR/2000/CR-SVG-20000802/DTD/svg-20000802.dtd”>
<svg xmlns=”http://www.w3.org/2000/svg” width=”300″ height=”150″>
<rect x=”231″ y=”61″ width=”9″ height=”12″ fitt=”#6e4a13″/>
<rect x=”107″ y=”106″ width=”56″ height=”5″ fitt=”#c406be”/>
…
</svg>
As you can see, each rectangle is described as a rect node. The position, width, height, and fill color are attributes. The fill color is an RGB value in hexadecimal.
Source: Horstmann Cay S. (2019), Core Java. Volume II – Advanced Features, Pearson; 11th edition.