The document object model is standardized by the W3C consortium, but its Java bindings can depend on the implementation. Java, since version 1.4, integrates XML processing packages that we use in our examples; no other special library is needed this way. The Java program given in Example 8.1 is a command line application that accepts an XML file as parameter and outputs the same compact text representation as the one used in Example 5.8 (page ) that we obtained with the XSLT stylesheet of Example 5.9.
The first lines of Example 8.1 import the necessary packages to
process the XML files. The main
method (line 26‑) creates a
DocumentBuilderFactory
object
(line 34‑) from which we will obtain a DOM
parser after having configured the necessary options. By default, parsing only checks
for well-formedness, so in order for the parsing to validate against a DTD a
flag must be set (line 36‑
). To validate against
an XML Schema another one must be set (line 38‑
). As explained in Section 3.1.1 and Section 3.5, the XML
instances reference their corresponding DTD or XML Schema.
Creating a parser to build a new DOM document is done (line 41‑) by using a factory method which returns a
DocumentBuilder
object to which an error handler (line 5‑
of Example 8.2) is
assigned to get a notification of possible error messages. If the file is valid (i.e. no
SAXParseException
is raised)[3], a Document
object can be obtained and the compact
method (line 83‑) is called (line 46‑
) on the root element.
In order to simplify further processing, we define (line 66‑) a method to remove from the DOM structure,
starting from a given node, all text nodes containing only spaces and carriage returns
and nodes other than element or text nodes, such as comments or processing instructions
nodes. This method goes through each children removing nodes (line 76‑
) that are not interesting for
further processing; some care has to be taken to save the next sibling (line 71‑
) before removing it from the DOM
because the removal has the side-effect of setting the sibling nodes to null. For nodes
that it does not remove, it calls itself recursively (line 78‑
) to strip-space from the current
child node. When it is called on the document element (line 45‑
), it will go through all the document to
remove empty text nodes and nodes that are not text or elements.
Within compact
(line 83‑), the
processing depends on the type of element obtained on line 85‑
. If it is an element node (line 86‑
), we first print the node name followed by an
opening bracket. On line 91‑
, attributes are printed
with their names preceded by an
@
, followed by their value in square
brackets. A new line is started if it is not the first attribute. The processing of the
children elements, starting on line 97‑, is a
simple traversal algorithm with a recursive call to
compact
(line 100‑), followed by the printing of a
closing bracket. In the case of a text node (line 106‑
), we print the content of the text node
removing leading and trailing spaces, which could be carriage returns or newlines.
Example 8.1. [DOMCompact.java
] Text compaction of the cellar book (Example 2.2) with Java using the DOM model
1 import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; 5 import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; 10 import org.xml.sax.InputSource; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; 15 import java.io.IOException; import javax.swing.JTree; import javax.swing.JFrame; 20 import javax.swing.JScrollPane; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; public class DOMCompact{ 25 public static void main(String argv[]) {// is there anything to do? if (argv.length != 1) { System.out.println("Usage: java DOMCompact file"); 30 System.exit(1); } // parse file try { DocumentBuilderFactory factory =
35 DocumentBuilderFactory.newInstance(); factory.setValidating(true);
factory.setNamespaceAware(true); factory.setAttribute(
"http://java.sun.com/xml/jaxp/properties/schemaLanguage", 40 "http://www.w3.org/2001/XMLSchema"); DocumentBuilder builder = factory.newDocumentBuilder();
builder.setErrorHandler(new CompactErrorHandler()); Document doc = builder.parse(argv[0]);
Node docElem=doc.getDocumentElement();
45 stripSpace(docElem);
compact(docElem,"");
System.out.println(); new TreeViewer(
new JTree(new DefaultTreeModel( 50 TreeViewer.jTreeBuild(docElem)))).setVisible(true); } catch (SAXParseException e) {
System.out.println(argv[0]+"is not well-formed"); System.out.println(e.getMessage()+"at line "+e.getLineNumber()+ ", column "+e.getColumnNumber()); 55 } catch (SAXException e){ System.out.println(e.getMessage()); } catch (ParserConfigurationException e){ System.out.println("Parser configuration error"); } catch (IOException e) { 60 System.out.println("IO Error on "+argv[0]); } } // remove empty text nodes (ie nothing else than spaces and carriage return) 65 // and nodes that are not text or element ones private static void stripSpace(Node node){
Node child = node.getFirstChild(); while(child!=null){ // save the sibling of the node that will 70 // perhaps be removed and set to null Node c = child.getNextSibling();
if((child.getNodeType()==Node.TEXT_NODE && child.getNodeValue().trim().length()==0) || ((child.getNodeType()!=Node.TEXT_NODE)&& 75 (child.getNodeType()!=Node.ELEMENT_NODE))) node.removeChild(child);
else // process children recursively stripSpace(child);
child=c; 80 } } public static void compact(Node node,String indent) {
if (node == null)return; 85 switch (node.getNodeType()) {
case Node.ELEMENT_NODE: {
System.out.print(node.getNodeName()+'['); indent += blanks(node.getNodeName().length()+1); NamedNodeMap attrs = node.getAttributes(); 90 boolean first=true; for (int i = 0; i < attrs.getLength(); i++) {
if(!first)System.out.print('\n'+indent); System.out.print('@'+attrs.item(i).getNodeName()+'[' +attrs.item(i).getNodeValue()+']'); 95 first=false; } for(Node child = node.getFirstChild();
child != null; child = child.getNextSibling()){ if(!first) System.out.print('\n'+indent); 100 compact(child,indent);
first=false; } System.out.print(']'); break; 105 } case Node.TEXT_NODE: {
System.out.print(node.getNodeValue().trim()); break; } 110 } } // production of string of spaces with a lazy StringBuffer private static StringBuffer blanks = new StringBuffer(); 115 private static String blanks(int n){
for(int i=blanks.length();i<n;i++) blanks.append(' '); return blanks.substring(0,n); } 120 }
|
Beginning of the Java program that checks if there is a parameter to be used as a file name to compact. |
|
Beginning of the parsing process creating a document builder that will be used for validation and for creating the DOM structure of the document. |
|
Indicates that document validation will be performed, on top of the default well-formedness check. |
|
Validation should be done with a Schema, not a DTD. |
|
Creates a class that will be used for creating the DOM structure. |
|
Parses the document using the previously created objects. |
|
Saves a reference to the root document element. |
|
Remove all white space nodes and non text or element nodes from the whole DOM structure. |
|
Creates a compact form by traversing the DOM structure from the root.
|
|
Creates an interactive window representing the DOM structure. |
|
Deals with exceptions that can occur during the parsing phase, such as non well-formedness, validation error or file-access exceptions. |
|
Empty text children nodes are removed so that only meaningful content nodes are compacted. |
|
Saves a reference to the next sibling. |
|
Removes the current child from the DOM structure. |
|
Strips space recursively on this child node. |
|
Prints a compact representation of the current node.
|
|
Determines the type of the current node in order to choose the appropriate processing step. |
|
An element node is first printed followed by an opening bracket and the indentation is updated so that recursive calls will print their content more indented that the name of the current node. |
|
Attributes are printed on separate (indented) lines; an attribute name
is prefixed with an |
|
The children are processed with the current indentation. Finally, a closing bracket is added. |
|
Compact this child recursively. |
|
Normalizes and prints a text node. |
|
Returns a string of a given number of spaces as a substring of static StringBuffer that is expanded as necessary. |
Example 8.2. [CompactErrorHandler.java
] Error handler of the DOM
parsing of Example 8.1
1 import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException;import org.xml.sax.SAXParseException; 5 public class CompactErrorHandler implements ErrorHandler{
private void message(String mess,SAXParseException e)
throws SAXException{ System.out.println("\n"+mess+ "\n Line:"+e.getLineNumber()+ 10 "\n URI:" +e.getSystemId()+ "\n Message:"+e.getMessage()); } public void fatalError(SAXParseException e) throws SAXException{
15 message("Fatal error",e); } public void error(SAXParseException e) throws SAXException{
message("Error",e); 20 } public void warning(SAXParseException e) throws SAXException{
message("Warning",e); } 25 }
|
|
|
Implementation of XML parsing error handling. |
|
Prints out an error message with the current error position. This method is used for all three kinds of errors. |
|
An irrecoverable error. |
|
An XML validation error. |
|
A simple warning not requiring processing to stop. |
[3] Even in the DOM model, SAXException
s and
SAXParseException
s can be raised by the document
builder.