DOM Parsing is where you read an XML file into a DOM-tree. This consists of Java nodes that denote the XML elements directly:
Document is the whole XML document,
Declaration is the XML declaration,
Instruction is a processing instruction,
Doctype is the doctype declaration,
Element is an XML element.
Attribute is an XML attribute,
Data is an escaped CDATA section,
Text is plain text,
Comment is an XML comment.
The DOM-tree nodes are in the Java package:
uk.ac.sheffield.jast.xml
See the Javadoc documentation for the API of these nodes. See below for an example.
XMLReader is the class to use for reading an XML file into a DOM-tree. It has four main constructors:
XMLReader(File file, String encoding)
XMLReader(URL url, String encoding)
XMLReader(InputStream stream,
String encoding)
XMLReader(Reader reader,
String encoding)
These allow you to read from different kinds of source (from a file, from a web URL, or from another stream). They all require the character encoding (e.g. "UTF-8") as a String.
XMLReader is in the top level package:
uk.ac.sheffield.jast
Reading from a local file is easily done using the following Java code:
File file =
new File("my/xml/input.xml");
XMLReader reader =
new XMLReader(file, "UTF-8");
Document document =
reader.readDocument();
reader.close();
After this, you may access the elements of the document using the API of the node classes.
Reading from a URL on the web is also easily done using the following Java code:
URL url = new URL(
"https://www.my.site/input.xml");
XMLReader reader =
new XMLReader(url, "ISO-8859-1");
Document document =
reader.readDocument();
reader.close();
This shows that you can specify a different character encoding Latin-1, which used to be standard for the HTTP protocol (probably UTF-8 now). Note that JAST raises an exception if the expected and actual encodings do not match.
After this, you may access the elements of the document using the API of the node classes.
By default, XMLReader ignores all formatting space between nodes, to keep the DOM-tree compact. If you wish to preserve the exact formatting layout in the DOM-tree, you may request this with the method preserveLayout():
File file =
new File("my/xml/input.xml");
XMLReader reader =
new XMLReader(file, "UTF-8");
reader.preserveLayout(true);
Document document =
reader.readDocument();
reader.close();
Note that you must request this before reading the Document. This will now include many more Text nodes that just contain whitespace in the DOM-tree.
Later, when you write out the DOM-tree, the original layout can be restored (see below).
XMLWriter is the class to use for writing a DOM-tree to an XML file. It has three main constructors:
XMLWriter(File file, String encoding)
XMLWriter(OutputStream stream,
String encoding)
XMLWriter(Writer writer,
String encoding)
These allow you to write to different kinds of sink (to a file, or to another stream). They all require the character encoding (e.g. "UTF-8") as a String.
XMLWriter is in the top level package:
uk.ac.sheffield.jast
Writing to a local file is easily done using the following Java code:
Document document = ... // exists
File file =
new File("my/xml/output.xml");
XMLWriter writer =
new XMLWriter(file, "UTF-8");
writer.writeDocument();
writer.close();
Remember always to close your writer.
Writing to a stream that uses a different encoding is possible with the following code:
Document document = ... // exists
OutputStream stream = ... // exists
XMLWriter writer =
new XMLWriter(stream, "ISO-8859-1");
writer.writeDocument();
writer.close();
This assumes that the existing output stream uses the Latin-1 encoding. Note that JAST throws an exception if the expected and actual encodings do not match.
By default, XMLWriter pretty-prints the DOM-tree using a standard indentation of two characters for each nested line. If you wish to preserve the layout of the DOM-tree, you may request this:
Document document = ... // exists
File file =
new File("my/xml/output.xml");
XMLWriter writer =
new XMLWriter(file, "UTF-8");
writer.preserveLayout(true);
writer.writeDocument();
writer.close();
This will preserve the layout captured in the DOM-tree. If this was originally read in preserving all whitespace formatting, then this will be restored.
Note that both the XMLReader and XMLWriter must ask to preserveLayout() for the original file format to be preserved. Otherwise, this instruction simply defeats the pretty-printing and the document will consist of one compact line of XML with no newlines or spaces.
In some applications, such as Java Servlets, you need to synchronise the character encodings used by the Servlet's PrintWriter and your XMLWriter.
Document document = ... // exists
HTTPServletResponse response = ... // Created by a Servlet
XMLWriter writer = new XMLWriter(
response.getWriter(),
response.getCharacterEncoding());
writer.writeDocument(document);
writer.close();
This will ensure that your XML output matches the encoding expected by the Servlet's PrintWriter.
The following is an example file of XML data, stored in the file: Family.xml.
<?xml version="1.0" encoding="UTF-8"?>
<Family>
<!-- The Smith family -->
<Person role="father" age="45">
John Smith
</Person>
<Person role="mother" age="41">
Mary Smith
</Person>
<Person role="son" age="16">
Ben Smith
</Person>
<Person role="daughter" age="14">
Alice Smith
</Person>
</Family>
Below, we show how to access different elements of this from the DOM-tree.
Once you have a valid Document object, you may navigate within the DOM-tree. Specific nodes under Document may be accessed:
Declaration header = // xml declaration
document.getDeclaration();
Doctype doctype = // optional doctype
document.getDoctype();
Comment comment = // optional comment
document.getComment();
Element root = // root element
document.getRootElement();
Here, the root Element is the one called Family. Subnnodes of the Document may be returned as a List of the more general type Content, or accessed by index:
List<Content> contents = // all subnodes
document.getContents();
Content node = // 3rd subnode
document.getContent(2);
The root Element may be queried to access its properties. An Element has a name and a type. Whereas getContents() returns subnodes of any kind, getChildren() returns only subnodes that are Elements. You may access all the child-Elements, or filter by a given element name, or find the first child with this name:
String name = root.getName();
int type = root.getType(); // type ID
List<Element> allChildren =
root.getChildren();
List<Element> someChildren =
root.getChildren("Person");
Element child =
root.getChild("Person");
You may navigate upwards in the DOM-tree. The parent of this child node is the original root node. Any Element may contain text. The text extracted from the first Person child is "John Smith".
Content parent = child.getParent();
String text = child.getText();
You may access all Attributes of an Element, or just one named Attribute, or the value of this Attribute:
List<Attribute> properties =
child.getAttributes();
Attribute property =
child.getAttribute("age");
String ageStr = child.getValue("age");
String ageStr = property.getValue();
int age = property.intValue();
The method getValue() returns a String. Attribute has more methods to convert values to other types.
The Document also has an iterator, which visits all nodes of the DOM-tree in-order. A version of this may also be requested with a Filter to iterate over specific subsets of nodes. Please see the Javadoc documentation for more details.
Constructing a DOM within Java is made easier by the fact that all methods to set or add values return the target object. This supports cascading and nesting method calls in sequence:
Document document = new Document();
Element root = new Element("Family")
.addContent(new Comment("The Smith Family")
.addContent(new Element("Person")
.setText("John Smith")
.setValue("role", "father")
.setValue("age", "45"))
.addContent(new Element("Person")
.addContent(new Text("Mary Smith"))
.setValue("role", "mother")
.setValue("age", "41"))
.addContent(new Element("Person")
.setText("Ben Smith")
.addAttribute(
new Attribute("role", "son"))
.addAttribute(
new Attribute("age", "16")))
.addContent(new Element("Person")
.addContent(new Text("Alice"))
.addContent(new Text(" Smith"))
setValue("role", "daughter")
setValue("age", "14"))
; // end of Family
document.setRootElement(root);
This example constructs as a single Document object corresponding to the original XML file, Family.xml. Note how there is no terminating semicolon until the end of the Family DOM-tree.
This example also demonstrates more than one way of adding text to an Element, and more than one way of setting attributes in an Element.
See the Javadoc Documentation for all methods that update nodes within the DOM-tree
XMLReader and XMLWriter raise certain Java exceptions if they encounter ill-formed XML or mis-matched character encodings. User programs must be prepared for these:
FileNotFoundException - if the requested file cannot be found;
UnsupportedEncodingException - if there is a mismatch between expected and actual character encodings;
IOException - if there is an underlying read or write failure while reading or writing a file;
SyntaxError - if XML data is syntactically incorrect, when reading a file;
SemanticError - if the construction rules for a DOM-tree are violated.
The last two are styled as fatal errors, rather than recoverable exceptions, since the W3C standard mandates this. A syntax error might be an XML opening tag with no matching closing tag. A semantic error might be trying to add a subnode to more than one parent node in the DOM-tree.