XML Infoset
Writing XML with Java
Reading XML through SAX2
Reading and Writing XML through the DOM
You need a JDK
You need some free class libraries
You need a text editor
You need some data to process
Are familiar with Java including I/O, classes, objects, polymorphism, etc.
Know XML including well-formedness, validity, namespaces, and so forth
I will briefly review proper terminology
Push: SAX, XNI
Tree: DOM, JDOM, XOM, dom4j, Sparta
Data binding: Castor, Zeus, JAXB
Pull: XMLPULL, StAX, NekoPull
Transform: XSLT, TrAX, XQuery
SAX, the Simple API for XML
SAX1
SAX2
DOM, the Document Object Model
DOM Level 0
DOM Level 1
DOM Level 2
DOM Level 3
JDOM
dom4j
XOM
TrAX
XMLPULL
StAX
Proprietary APIs
Parser specific APIs
Sun's Java API for XML Parsing = SAX1 + DOM1 + a few factory classes
JSR-000031 XML Data Binding Specification from Bluestone, Sun, webMethods et al.
The proposed specification will define an XML data-binding facility for the JavaTM Platform. Such a facility compiles an XML schema into one or more Java classes. These automatically-generated classes handle the translation between XML documents that follow the schema and interrelated instances of the derived classes. They also ensure that the constraints expressed in the schema are maintained as instances of the classes are manipulated.
The Infoset is the unfortunate standard to which those in retreat from the radical and most useful implications of well-formedness have rallied. At its core the Infoset insists that there is 'more' to XML than the straightforward syntax of well-formedness. By imposing its canonical semantics the Infoset obviates the infinite other semantic outcomes which might be elaborated in particular unique circumstances from an instance of well-formed XML 1.0 syntax. The question we should be asking is not whether the Infoset has chosen the correct canonical semantics, but whether the syntactic possibilities of XML 1.0 should be curtailed in this way at all.--Walter Perry on the xml-dev mailing list
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/css" href="song.css"?>
<!DOCTYPE SONG SYSTEM "song.dtd">
<SONG xmlns="http://www.cafeconleche.org/namespace/song"
xmlns:xlink="http://www.w3.org/1999/xlink">
<TITLE>Hot Cop</TITLE>
<PHOTO
xlink:type="simple" xlink:show="onLoad" xlink:href="hotcop.jpg"
ALT="Victor Willis in Cop Outfit" WIDTH="100" HEIGHT="200"/>
<COMPOSER>Jacques Morali</COMPOSER>
<COMPOSER>Henri Belolo</COMPOSER>
<COMPOSER>Victor Willis</COMPOSER>
<PRODUCER>Jacques Morali</PRODUCER>
<!-- The publisher is actually Polygram but I needed
an example of a general entity reference. -->
<PUBLISHER xlink:type="simple" xlink:href="http://www.amrecords.com/">
A & M Records
</PUBLISHER>
<LENGTH>6:20</LENGTH>
<YEAR>1978</YEAR>
<ARTIST>Village People</ARTIST>
</SONG>
<!-- You can tell what album I was
listening to when I wrote this example -->
View in BrowserMarkup includes:
Tags
Entity References
Comments
Processing Instructions
Document Type Declarations
XML Declaration
CDATA Section Delimiters
Character data includes everything else
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/css" href="song.css"?>
<!DOCTYPE SONG SYSTEM "song.dtd">
<SONG xmlns="http://www.cafeconleche.org/namespace/song"
xmlns:xlink="http://www.w3.org/1999/xlink">
<TITLE>Hot Cop</TITLE>
<PHOTO
xlink:type="simple" xlink:show="onLoad" xlink:href="hotcop.jpg"
ALT="Victor Willis in Cop Outfit" WIDTH="100" HEIGHT="200"/>
<COMPOSER>Jacques Morali</COMPOSER>
<COMPOSER>Henri Belolo</COMPOSER>
<COMPOSER>Victor Willis</COMPOSER>
<PRODUCER>Jacques Morali</PRODUCER>
<!-- The publisher is actually Polygram but I needed
an example of a general entity reference. -->
<PUBLISHER xlink:type="simple" xlink:href="http://www.amrecords.com/">
A & M Records
</PUBLISHER>
<LENGTH>6:20</LENGTH>
<YEAR>1978</YEAR>
<ARTIST>Village People</ARTIST>
</SONG>
<!-- You can tell what album I was
listening to when I wrote this example -->
Elements are delimited by a start-tag like
<LENGTH>
and a matching end-tag
like </LENGTH>
:
<LENGTH>6:20</LENGTH>
Elements contain content which can be text, child elements, or both:
<LENGTH>6:20</LENGTH>
<PRODUCER>
<NAME><GIVEN>Jacques</GIVEN> <FAMILY>Morali</FAMILY></NAME>
</PRODUCER>
<PARAGRAPH>
The <ARTIST>Village People</ARTIST>
were a popular <GENRE>Disco</GENRE> band in the 1970's
</PARAGRAPH>
The element is the tags plus the content.
Empty-element tags both start and end an element:
<PHOTO/>
<PHOTO></PHOTO>
Elements can have attributes:
<PHOTO
xlink:type="simple" xlink:show="onLoad" xlink:href="hotcop.jpg"
ALT="Victor Willis in Cop Outfit" WIDTH="100" HEIGHT="200"/>
An XML document is made up of one or more physical storage units called entities
Entity references:
Parsed internal general entity references like &
Parsed external general entity references
Unparsed external general entity references
External parameter entity references
Internal parameter entity references
Reading an XML document is not the same thing as reading an XML file
The file contains entity references.
The document contains the entities' replacement text.
When you use a parser to read a document you'll get the text including characters like <. You will not see the entity references.
Character data left after entity references are replaced with their text
Given the element
<PUBLISHER>A & M Records</PUBLISHER>
The parsed character data is
A & M Records
Used to include large blocks of text with lots of normally
illegal literal characters like
<
and &
, typically XML or HTML.
<p>You can use a default <code>xmlns</code>
attribute to avoid having to add the svg
prefix to all
your elements:</p>
<![CDATA[
<svg xmlns="http://www.w3.org/2000/svg"
width="12cm" height="10cm">
<ellipse rx="110" ry="130" />
<rect x="4cm" y="1cm" width="3cm" height="6cm" />
</svg>
]]>
CDATA is for human authors, not for programs!
<!-- Before posting this page, I need to double check the number
of pelicans in Lousiana in 1970 -->
Comments are for humans, not programs.
Divided into a target and data for the target
The target must be an XML name
The data can have an effectively arbitrary format
<?robots index="yes" follow="no"?>
<?xml-stylesheet href="pelicans.css" type="text/css"?>
<?php
mysql_connect("database.unc.edu", "clerk", "password");
$result = mysql("CYNW", "SELECT LastName, FirstName FROM Employees
ORDER BY LastName, FirstName");
$i = 0;
while ($i < mysql_numrows ($result)) {
$fields = mysql_fetch_row($result);
echo "<person>$fields[1] $fields[0] </person>\r\n";
$i++;
}
mysql_close();
?>
These are for programs
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
Looks like a processing instruction but isn't.
version
attribute
required
should have the value 1.0
encoding
attribute
UTF-8
ISO-8859-1
SJIS
etc.
standalone
attribute
yes
no
<!DOCTYPE SONG SYSTEM "song.dtd">
<!ELEMENT SONG (TITLE, PHOTO?, COMPOSER+, PRODUCER*,
PUBLISHER*, LENGTH?, YEAR?, ARTIST+)>
<!ELEMENT TITLE (#PCDATA)>
<!ELEMENT COMPOSER (#PCDATA)>
<!ELEMENT PRODUCER (#PCDATA)>
<!ELEMENT PUBLISHER (#PCDATA)>
<!ELEMENT LENGTH (#PCDATA)>
<!-- This should be a four digit year like "1999",
not a two-digit year like "99" -->
<!ELEMENT YEAR (#PCDATA)>
<!ELEMENT ARTIST (#PCDATA)>
<!ELEMENT PHOTO EMPTY>
<!ATTLIST PHOTO xlink:type (simple) #FIXED "simple"
xlink:show (onLoad) #FIXED "onLoad"
xlink:href CDATA #REQUIRED
ALT CDATA #REQUIRED
WIDTH NMTOKEN #REQUIRED
HEIGHT NMTOKEN #REQUIRED
>
<!ATTLIST PUBLISHER xlink:type (simple) #FIXED "simple"
xlink:href CDATA #REQUIRED
>
<!ATTLIST SONG xmlns CDATA #FIXED "http://www.cafeconleche.org/namespace/song"
xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink"
>
Used for element, attribute, and entity names
Can contain any Unicode 2.0 alphabetic, ideographic, or numeric Unicode character
Can contain hyphen, underscore, or period
Can also contain colons but these are reserved for namespaces
Can begin with any Unicode 2.0 alphabetic or ideographic character or the underscore but not digits or other punctuation marks
Raison d'etre:
To distinguish between elements and attributes from different vocabularies with different meanings.
To group all related elements and attributes together so that a parser can easily recognize them.
Each element is given a prefix
Each prefix (as well as the empty prefix) is associated with a URI
Elements with the same URI are in the same namespace
URIs are purely formal. They do not necessarily point to a page.
Elements and attributes that are in namespaces have names that contain exactly one colon. They look like this:
rdf:description
xlink:type
xsl:template
Everything before the colon is called the prefix
Everything after the colon is called the local part or local name.
The complete name including the colon is called the qualified name or raw name.
Each prefix in a qualified name is associated with a URI.
For example, all elements in XSLT 1.0 style sheets are associated with the http://www.w3.org/1999/XSL/Transform URI.
The customary prefix xsl
is a shorthand for the longer URI
http://www.w3.org/1999/XSL/Transform.
You can't use the URI in the element name directly.
Prefixes are bound to namespace URIs by attaching an xmlns:prefix
attribute to the prefixed element or one of its ancestors.
<svg:svg xmlns:svg="http://www.w3.org/2000/svg"
width="12cm" height="10cm">
<svg:ellipse rx="110" ry="130" />
<svg:rect x="4cm" y="1cm" width="3cm" height="6cm" />
</svg:svg>
Bindings have scope within the element where they're declared.
An SVG processor can recognize all three of these elements as SVG elements because they all have prefixes bound to the particular URI defined by the SVG specification.
Indicate that an unprefixed element and all its unprefixed descendant
elements belong to a particular namespace by attaching an xmlns
attribute with no prefix:
<DATASCHEMA xmlns="http://www.w3.org/2000/P3Pv1">
<DATA name="vehicle.make" type="text" short="Make"
category="preference" size="31"/>
<DATA name="vehicle.model" type="text" short="Model"
category="preference" size="31"/>
<DATA name="vehicle.year" type="number" short="Year"
category="preference" size="4"/>
<DATA name="vehicle.license.state." type="postal." short="State"
category="preference" size="2"/>
<DATA name="vehicle.license.number" type="text"
short="License Plate Number" category="preference" size="12"/>
</DATASCHEMA>
Both the DATASCHEMA
and DATA
elements are in the
http://www.w3.org/2000/P3Pv1 namespace.
Default namespaces apply only to elements, not to attributes.
Thus in the above example the name
,
type
, short
, category
, and size
attributes are not in any namespace.
Unprefixed attributes are never in any namespace.
You can change the default namespace within a particular
element by adding an xmlns
attribute to the element.
Namespaces were added to XML 1.0 after the fact, but care was taken to ensure backwards compatibility.
An XML 1.0 parser that does not know about namespaces will most likely not have any troubles reading a document that uses namespaces.
A namespace aware parser also checks to see that all prefixes are mapped to URIs. Otherwise it behaves almost exactly like a non-namespace aware parser.
Other software that sits on top of the raw XML parser, an XSLT engine for example, may treat elements differently depending on what namespace they belong to. However, the XML parser itself mostly doesn't care as long as all well-formedness and namespace constraints are met.
A possible exception occurs in the unlikely event that elements with different prefixes belong to the same namespace or elements with the same prefix belong to different namespaces
Many parsers have the option of whether to report namespace violations so that you can turn namespace processing on or off as you see fit.
Semantics |
Structure |
Syntax |
Lexical |
Binary |
I have learned to be even more skeptical than before about the slew of APIs doing the rounds in the XML development community. An XML instance is just a documents, guys; you need to understand the document structure and document interchange choreography of your systems. Don't let some API get in the way of your understanding of XML systems at the document level. If you do, you run the risk becoming a slave to the APIs and hitting a wall when the APIs fail you.
--Sean McGrath
Read the rest in ITworld.com - XML IN PRACTICE - APIs Considered Harmful
XML documents are text
Any Writer
can produce an XML document
XML documents and APIs are Unicode
Unicode encodings:
UTF-8
UTF-16 big endian
UCS-4 big endian
UTF-16 little endian
UCS-4 little endian
Non-Unicode encodings:
ASCII (subset of UTF-8)
MacRoman
Windows ANSI
Latin 1 through Latin 15
SJIS Japanese
Big-5 Chinese
K0I8R Cyrillic
Many others...
Java's InputStreamReader
and OutputStreamWriter
classes are very helpful
URL u = new URL(
"http://www.ascc.net/xml/test/wfdtd/utf-8/application_xml/zh-utf8-8.xml");
InputStream in = u.openStream();
InputStreamReader reader = new InputStreamReader(in, "UTF-8");
int c;
while ((c = in.read()) != -1) System.out.write(c);
import java.math.BigInteger;
import java.io.*;
public class FibonacciText {
public static void main(String[] args) {
try {
OutputStream fout = new FileOutputStream("fibonacci.txt");
Writer out = new OutputStreamWriter(fout, "8859_1");
BigInteger low = BigInteger.ONE;
BigInteger high = BigInteger.ONE;
for (int i = 1; i <= 25; i++) {
out.write(low.toString() + "\r\n");
BigInteger temp = high;
high = high.add(low);
low = temp;
}
out.write(high.toString() + "\r\n");
out.close();
}
catch (IOException e) {
System.err.println(e);
}
}
}
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
317811
import java.math.BigInteger;
import java.io.*;
public class FibonacciXML {
public static void main(String[] args) {
try {
OutputStream fout = new FileOutputStream("fibonacci.xml");
Writer out = new OutputStreamWriter(fout, "UTF-8");
BigInteger low = BigInteger.ONE;
BigInteger high = BigInteger.ONE;
out.write("<?xml version=\"1.0\"?>\r\n");
out.write("<Fibonacci_Numbers>\r\n");
for (int i = 1; i <= 25; i++) {
out.write(" <fibonacci index=\"" + i + "\">");
out.write(low.toString());
out.write("</fibonacci>\r\n");
BigInteger temp = high;
high = high.add(low);
low = temp;
}
out.write("</Fibonacci_Numbers>");
out.close();
}
catch (IOException ex) {
System.err.println(ex);
}
}
}
<?xml version="1.0"?>
<Fibonacci_Numbers>
<fibonacci index="1">1</fibonacci>
<fibonacci index="2">1</fibonacci>
<fibonacci index="3">2</fibonacci>
<fibonacci index="4">3</fibonacci>
<fibonacci index="5">5</fibonacci>
<fibonacci index="6">8</fibonacci>
<fibonacci index="7">13</fibonacci>
<fibonacci index="8">21</fibonacci>
<fibonacci index="9">34</fibonacci>
<fibonacci index="10">55</fibonacci>
<fibonacci index="11">89</fibonacci>
<fibonacci index="12">144</fibonacci>
<fibonacci index="13">233</fibonacci>
<fibonacci index="14">377</fibonacci>
<fibonacci index="15">610</fibonacci>
<fibonacci index="16">987</fibonacci>
<fibonacci index="17">1597</fibonacci>
<fibonacci index="18">2584</fibonacci>
<fibonacci index="19">4181</fibonacci>
<fibonacci index="20">6765</fibonacci>
<fibonacci index="21">10946</fibonacci>
<fibonacci index="22">17711</fibonacci>
<fibonacci index="23">28657</fibonacci>
<fibonacci index="24">46368</fibonacci>
<fibonacci index="25">75025</fibonacci>
</Fibonacci_Numbers>
import java.math.BigInteger;
import java.io.*;
public class FibonacciApos {
public static void main(String[] args) {
try {
OutputStream fout = new FileOutputStream("fibonacci_apos.xml");
Writer out = new OutputStreamWriter(fout);
BigInteger low = BigInteger.ONE;
BigInteger high = BigInteger.ONE;
out.write("<?xml version='1.0'?>\r\n");
out.write("<Fibonacci_Numbers>\r\n");
for (int i = 1; i <= 25; i++) {
out.write(" <fibonacci index='" + i + "'>");
out.write(low.toString());
out.write("</fibonacci>\r\n");
BigInteger temp = high;
high = high.add(low);
low = temp;
}
out.write("</Fibonacci_Numbers>");
out.close();
}
catch (IOException e) {
System.err.println(e);
}
}
}
import java.math.BigInteger;
import java.io.*;
public class FibonacciLatin1 {
public static void main(String[] args) {
try {
OutputStream fout = new FileOutputStream("fibonacci_Latin_1.xml");
Writer out = new OutputStreamWriter(fout, "8859_1");
BigInteger low = BigInteger.ONE;
BigInteger high = BigInteger.ONE;
out.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n");
out.write("<Fibonacci_Numbers>\r\n");
for (int i = 1; i <= 25; i++) {
out.write(" <fibonacci index=\"" + i + "\">");
out.write(low.toString());
out.write("</fibonacci>\r\n");
BigInteger temp = high;
high = high.add(low);
low = temp;
}
out.write("</Fibonacci_Numbers>");
out.close();
}
catch (IOException e) {
System.err.println(e);
}
}
}
<?xml version="1.0" encoding="ISO-8859-1"?>
<Fibonacci_Numbers>
<fibonacci index="1">1</fibonacci>
<fibonacci index="2">1</fibonacci>
<fibonacci index="3">2</fibonacci>
<fibonacci index="4">3</fibonacci>
<fibonacci index="5">5</fibonacci>
<fibonacci index="6">8</fibonacci>
<fibonacci index="7">13</fibonacci>
<fibonacci index="8">21</fibonacci>
<fibonacci index="9">34</fibonacci>
<fibonacci index="10">55</fibonacci>
<fibonacci index="11">89</fibonacci>
<fibonacci index="12">144</fibonacci>
<fibonacci index="13">233</fibonacci>
<fibonacci index="14">377</fibonacci>
<fibonacci index="15">610</fibonacci>
<fibonacci index="16">987</fibonacci>
<fibonacci index="17">1597</fibonacci>
<fibonacci index="18">2584</fibonacci>
<fibonacci index="19">4181</fibonacci>
<fibonacci index="20">6765</fibonacci>
<fibonacci index="21">10946</fibonacci>
<fibonacci index="22">17711</fibonacci>
<fibonacci index="23">28657</fibonacci>
<fibonacci index="24">46368</fibonacci>
<fibonacci index="25">75025</fibonacci>
</Fibonacci_Numbers>
import java.math.BigInteger;
import java.io.*;
public class FibonacciDTD {
public static void main(String[] args) {
try {
OutputStream fout = new FileOutputStream("valid_fibonacci.xml");
Writer out = new OutputStreamWriter(fout, "UTF-8");
BigInteger low = BigInteger.ONE;
BigInteger high = BigInteger.ONE;
out.write("<?xml version=\"1.0\"?>\r\n");
out.write("<!DOCTYPE Fibonacci_Numbers [\r\n");
out.write(" <!ELEMENT Fibonacci_Numbers (fibonacci*)>\r\n");
out.write(" <!ELEMENT fibonacci (#PCDATA)>\r\n");
out.write(" <!ATTLIST fibonacci index CDATA #IMPLIED>\r\n");
out.write("]>\r\n");
out.write("<Fibonacci_Numbers>\r\n");
for (int i = 1; i <= 25; i++) {
out.write(" <fibonacci index=\"" + i + "\">");
out.write(low.toString());
out.write("</fibonacci>\r\n");
BigInteger temp = high;
high = high.add(low);
low = temp;
}
out.write("</Fibonacci_Numbers>");
out.close();
}
catch (IOException e) {
System.err.println(e);
}
}
}
<?xml version="1.0"?>
<!DOCTYPE Fibonacci_Numbers [
<!ELEMENT Fibonacci_Numbers (fibonacci*)>
<!ELEMENT fibonacci (#PCDATA)>
<!ATTLIST fibonacci index CDATA #IMPLIED>
]>
<Fibonacci_Numbers>
<fibonacci index="0">0</fibonacci>
<fibonacci index="1">1</fibonacci>
<fibonacci index="2">1</fibonacci>
<fibonacci index="3">2</fibonacci>
<fibonacci index="4">3</fibonacci>
<fibonacci index="5">5</fibonacci>
<fibonacci index="6">8</fibonacci>
<fibonacci index="7">13</fibonacci>
<fibonacci index="8">21</fibonacci>
<fibonacci index="9">34</fibonacci>
<fibonacci index="10">55</fibonacci>
<fibonacci index="11">89</fibonacci>
<fibonacci index="12">144</fibonacci>
<fibonacci index="13">233</fibonacci>
<fibonacci index="14">377</fibonacci>
<fibonacci index="15">610</fibonacci>
<fibonacci index="16">987</fibonacci>
<fibonacci index="17">1597</fibonacci>
<fibonacci index="18">2584</fibonacci>
<fibonacci index="19">4181</fibonacci>
<fibonacci index="20">6765</fibonacci>
<fibonacci index="21">10946</fibonacci>
<fibonacci index="22">17711</fibonacci>
<fibonacci index="23">28657</fibonacci>
<fibonacci index="24">46368</fibonacci>
</Fibonacci_Numbers>
Elliotte Rusty Harold
Addison Wesley, 2002
Chapters 3-4:
Chapter 3, Writing XML with Java: http://www.cafeconleche.org/books/xmljava/chapters/ch03.html
Chapter 4, Converting Flat Files to XML: http://www.cafeconleche.org/books/xmljava/chapters/ch04.html
For streams and readers and writers:
Java I/O
Elliotte Rusty Harold
O'Reilly & Associates, 1999
ISBN: 1-56592-485-1
For well-formedness rules and such: XML in a Nutshell, third edition
Elliotte Rusty Harold and W. Scott Means
O'Reilly & Associates, 2004
ISBN 0-596-00764-7
Actually, SAX2 has ** MUCH ** better infoset support than DOM does. Yes, I've done the detailed analysis.
--David Brownell on the xml-dev mailing list
The stereotypical "Desperate Perl Hacker" (DPH) is supposed to be able to write an XML parser in a weekend.
The parser does the hard work for you.
Your code reads the document through the parser's API.
Public domain, developed on xml-dev mailing list
Maintained by David Megginson
org.xml.sax package
Event based
Parser | URL | Validating | Namespaces | SAX1 | SAX2 | License |
---|---|---|---|---|---|---|
Yuval Oren's Piccolo | http://piccolo.sourceforge.net/ | X | X | X | LGPL | |
Apache XML Project's Xerces Java | http://xml.apache.org/xerces2-j/index.html | X | X | X | X | Apache Software License, Version 1.1 |
IBM's XML for Java | http://www.alphaworks.ibm.com/formula/xml | X | X | X | X | Apache Software License, Version 1.1 |
Ælfred | http://www.gnu.org/software/classpathx/jaxp/jaxp.html | X | X | X | X | GPL with library exception |
Sun's Crimson | http://xml.apache.org/crimson/ | X | X | X | X | Apache |
Oracle's XML Parser for Java | http://technet.oracle.com/ | X | X | X | X | free beer |
Caucho Resin | http://www.caucho.com/products/resin-xml/index.xtp | ? | X | X | X | payware |
Saxon's AElfred | http://saxon.sourceforge.net/aelfred.html | X | X | X | BSD-ish license |
Java 1.4.x bundle Crimson and Xalan
These are loaded before anything in the CLASSPATH or jre/lib/ext directory
Use jre/lib/endorsed to override (You must create this directory.)
Saxon is incompatible with Ant.
SAX1 omits:
Comments
Lexical Information (CDATA sections, entity references, etc.)
DTD declarations
Validation
Namespaces
Adds:
Namespace support
Optional validation
Optional lexical events for comments, CDATA sections, entity references
A lot more configurable
Deprecates a lot of SAX1
Adapter classes convert between parsers.
Use the factory method
XMLReaderFactory.createXMLReader()
to retrieve a parser-specific implementation of the
XMLReader
interface
Your code registers a ContentHandler
with the parser
An InputSource
feeds the document into the parser
As the document is read, the parser calls back to the
methods of the ContentHandler
to tell it
what it's seeing in the document.
The XMLReaderFactory.createXMLReader()
method
instantiates an XMLReader
subclass named by
the org.xml.sax.driver
system property:
try {
XMLReader parser = XMLReaderFactory.createXMLReader();
}
catch (SAXException e) {
System.err.println(e);
}
or
System.setProperty("org.xml.sax.driver", "org.apache.xerces.parsers.SAXParser");
try {
XMLReader parser = XMLReaderFactory.createXMLReader();
}
catch (SAXException e) {
System.err.println(e);
}
The XMLReaderFactory.createXMLReader(String className)
method
instantiates an XMLReader
subclass named by
its argument:
try {
XMLReader parser
= XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser");
}
catch (SAXException e) {
System.err.println(e);
}
Or you can use the constructor in the package-specific class:
XMLReader parser = new org.apache.xerces.parsers.SAXParser();
Or all three:
XMLReader parser;
try {
parser = XMLReaderFactory.createXMLReader();
}
catch (SAXException ex) {
try {
parser = XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser");
}
catch (SAXException ex2) {
parser = new org.apache.xerces.parsers.SAXParser();
}
}
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.io.*;
public class SAX2Checker {
public static void main(String[] args) {
XMLReader parser;
try {
parser = XMLReaderFactory.createXMLReader();
}
catch (SAXException ex) {
try {
parser = XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser");
}
catch (SAXException ex2) {
System.out.println("Could not locate a parser."
+ "Please set the the org.xml.sax.driver property.");
return;
}
}
if (args.length == 0) {
System.out.println("Usage: java SAX2Checker URL1 URL2...");
}
// start parsing...
for (int i = 0; i < args.length; i++) {
// command line should offer URIs or file names
try {
parser.parse(args[i]);
// If there are no well-formedness errors
// then no exception is thrown
System.out.println(args[i] + " is well formed.");
}
catch (SAXParseException e) { // well-formedness error
System.out.println(args[i] + " is not well formed.");
System.out.println(e.getMessage()
+ " at line " + e.getLineNumber()
+ ", column " + e.getColumnNumber());
}
catch (SAXException e) { // some other kind of error
System.out.println(e.getMessage());
}
catch (IOException e) {
System.out.println("Could not check " + args[i]
+ " because of the IOException " + e);
}
}
}
}
C:\>java SAX2Checker http://www.cafeconleche.org/
http://www.cafeconleche.org/ is not well formed.
The element type "dt" must be terminated by the
matching end-tag "</dt>".
at line 186, column 5
Under no circumstances, should you ever use javax.xml.parsers.SAXParser
or SAXParserFactory
These classes were designed to fill holes in SAX1. They are unnecessary and actively harmful when working with SAX2.
Use XMLReader
and XMLReaderFactory
instead
package org.xml.sax;
public interface ContentHandler {
public void setDocumentLocator(Locator locator);
public void startDocument() throws SAXException;
public void endDocument() throws SAXException;
public void startPrefixMapping(String prefix, String uri)
throws SAXException;
public void endPrefixMapping(String prefix) throws SAXException;
public void startElement(String namespaceURI, String localName,
String qualifiedName, Attributes atts) throws SAXException;
public void endElement(String namespaceURI, String localName,
String qualifiedName) throws SAXException;
public void characters(char[] text, int start, int length)
throws SAXException;
public void ignorableWhitespace(char[] text, int start, int length)
throws SAXException;
public void processingInstruction(String target, String data)
throws SAXException;
public void skippedEntity(String name) throws SAXException;
}
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.io.*;
public class EventReporter implements ContentHandler {
public void setDocumentLocator(Locator locator) {
System.out.println("setDocumentLocator(" + locator + ")");
}
public void startDocument() throws SAXException {
System.out.println("startDocument()");
}
public void endDocument() throws SAXException {
System.out.println("endDocument()");
}
public void startElement(String namespaceURI, String localName,
String qualifiedName, Attributes atts)
throws SAXException {
namespaceURI = '"' + namespaceURI + '"';
localName = '"' + localName + '"';
qualifiedName = '"' + qualifiedName + '"';
String attributeString = "{";
for (int i = 0; i < atts.getLength(); i++) {
attributeString += atts.getQName(i) + "=\""
+ atts.getValue(i) + "\"";
if (i != atts.getLength()-1) attributeString += ", ";
}
attributeString += "}";
System.out.println("startElement(" + namespaceURI + ", "
+ localName + ", " + qualifiedName + ", " + attributeString + ")");
}
public void endElement(String namespaceURI, String localName,
String qualifiedName)
throws SAXException {
namespaceURI = '"' + namespaceURI + '"';
localName = '"' + localName + '"';
qualifiedName = '"' + qualifiedName + '"';
System.out.println("endElement(" + namespaceURI + ", "
+ localName + ", " + qualifiedName + ")");
}
public void characters(char[] text, int start, int length)
throws SAXException {
String textString = "[" + new String(text) + "]";
System.out.println("characters(" + textString + ", "
+ start + ", " + length + ")");
}
public void ignorableWhitespace(char[] text, int start, int length)
throws SAXException {
System.out.println("ignorableWhitespace()");
}
public void processingInstruction(String target, String data)
throws SAXException {
System.out.println("processingInstruction(" + target + ", "
+ data + ")");
}
public void startPrefixMapping(String prefix, String uri)
throws SAXException {
System.out.println("startPrefixMapping(\"" + prefix + "\", \""
+ uri + "\")");
}
public void endPrefixMapping(String prefix) throws SAXException {
System.out.println("endPrefixMapping(\"" + prefix + "\")");
}
public void skippedEntity(String name) throws SAXException {
System.out.println("skippedEntity(" + name + ")");
}
// Could easily have put main() method in a separate class
public static void main(String[] args) {
XMLReader parser;
try {
parser = XMLReaderFactory.createXMLReader();
}
catch (Exception e) {
// fall back on Xerces parser by name
try {
parser = XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser");
}
catch (Exception ee) {
System.err.println("Couldn't locate a SAX parser");
return;
}
}
if (args.length == 0) {
System.out.println(
"Usage: java EventReporter URL1 URL2...");
}
// Install the content handler
parser.setContentHandler(new EventReporter());
// start parsing...
for (int i = 0; i < args.length; i++) {
// command line should offer URIs or file names
try {
parser.parse(args[i]);
}
catch (SAXParseException e) { // well-formedness error
System.out.println(args[i] + " is not well formed.");
System.out.println(e.getMessage()
+ " at line " + e.getLineNumber()
+ ", column " + e.getColumnNumber());
}
catch (SAXException e) { // some other kind of error
System.out.println(e.getMessage());
}
catch (IOException e) {
System.out.println("Could not report on " + args[i]
+ " because of the IOException " + e);
}
}
}
}
UserLand's RSS based list of Web logs at http://static.userland.com/weblogMonitor/logs.xml:
<?xml version="1.0"?>
<!-- <!DOCTYPE foo SYSTEM "http://msdn.microsoft.com/xml/general/htmlentities.dtd"> -->
<weblogs>
<log>
<name>MozillaZine</name>
<url>http://www.mozillazine.org</url>
<changesUrl>http://www.mozillazine.org/contents.rdf</changesUrl>
<ownerName>Jason Kersey</ownerName>
<ownerEmail>kerz@en.com</ownerEmail>
<description>THE source for news on the Mozilla Organization. DevChats, Reviews, Chats, Builds, Demos, Screenshots, and more.</description>
<imageUrl></imageUrl>
<adImageUrl>http://static.userland.com/weblogMonitor/ads/kerz@en.com.gif</adImageUrl>
</log>
<log>
<name>SalonHerringWiredFool</name>
<url>http://www.salonherringwiredfool.com/</url>
<ownerName>Some Random Herring</ownerName>
<ownerEmail>salonfool@wiredherring.com</ownerEmail>
<description></description>
</log>
<log>
<name>Scripting News</name>
<url>http://www.scripting.com/</url>
<ownerName>Dave Winer</ownerName>
<ownerEmail>dave@userland.com</ownerEmail>
<description>News and commentary from the cross-platform scripting community.</description>
<imageUrl>http://www.scripting.com/gifs/tinyScriptingNews.gif</imageUrl>
<adImageUrl>http://static.userland.com/weblogMonitor/ads/dave@userland.com.gif</adImageUrl>
</log>
<log>
<name>SlashDot.Org</name>
<url>http://www.slashdot.org/</url>
<ownerName>Simply a friend</ownerName>
<ownerEmail>afriendofweblogs@weblogs.com</ownerEmail>
<description>News for Nerds, Stuff that Matters.</description>
</log>
</weblogs>
Design Decisions
Should we return an array, an Enumeration
,
a List
, or what?
Perhaps we should use multiple threads?
We do not know how many URLs there will be when we start parsing
so let's use a Vector
Single threaded for simplicity but a real program would use multiple threads
One to load and parse the data
Another thread (probably the main thread) to serve the data
Early data could be provided before the entire document had been read
The character data of each url
element needs to be stored.
Everything else can be ignored.
A startElement()
with the name
url indicates that we need to start
storing this data.
A stopElement()
with the name url indicates that we need to stop
storing this data, convert it to a URL
and put it in the
Vector
Should we hide the XML parsing inside a non-public class to avoid accidentally calling the methods from unexpected places or threads?
import org.xml.sax.*;
import org.xml.sax.helpers.XMLReaderFactory;
import java.util.*;
import java.io.*;
public class WeblogsSAX {
public static List listChannels()
throws IOException, SAXException {
return listChannels(
"http://static.userland.com/weblogMonitor/logs.xml");
}
public static List listChannels(String uri)
throws IOException, SAXException {
XMLReader parser;
try {
parser = XMLReaderFactory.createXMLReader();
}
catch (SAXException ex) {
parser = XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser"
);
}
Vector urls = new Vector(1000);
ContentHandler handler = new URIGrabber(urls);
parser.setContentHandler(handler);
parser.parse(uri);
return urls;
}
public static void main(String[] args) {
try {
List urls;
if (args.length > 0) urls = listChannels(args[0]);
else urls = listChannels();
Iterator iterator = urls.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
catch (IOException e) {
System.err.println(e);
}
catch (SAXParseException e) {
System.err.println(e);
System.err.println("at line " + e.getLineNumber()
+ ", column " + e.getColumnNumber());
}
catch (SAXException e) {
System.err.println(e);
}
catch (/* Unexpected */ Exception e) {
e.printStackTrace();
}
}
}
import org.xml.sax.*;
import java.net.*;
import java.util.Vector;
// conflicts with java.net.ContentHandler
class URIGrabber implements org.xml.sax.ContentHandler {
private Vector urls;
URIGrabber(Vector urls) {
this.urls = urls;
}
// do nothing methods
public void setDocumentLocator(Locator locator) {}
public void startDocument() throws SAXException {}
public void endDocument() throws SAXException {}
public void startPrefixMapping(String prefix, String uri)
throws SAXException {}
public void endPrefixMapping(String prefix) throws SAXException {}
public void skippedEntity(String name) throws SAXException {}
public void ignorableWhitespace(char[] text, int start, int length)
throws SAXException {}
public void processingInstruction(String target, String data)
throws SAXException {}
// Remember, there's no guarantee all the text of the
// url element will be returned in a single call to characters
private StringBuffer urlBuffer;
private boolean collecting = false;
public void startElement(String namespaceURI, String localName,
String qualifiedName, Attributes atts) throws SAXException {
if (qualifiedName.equals("url")) {
collecting = true;
urlBuffer = new StringBuffer();
}
}
public void characters(char[] text, int start, int length)
throws SAXException {
if (collecting) {
urlBuffer.append(text, start, length);
}
}
public void endElement(String namespaceURI, String localName,
String qualifiedName) throws SAXException {
if (qualifiedName.equals("url")) {
collecting = false;
String url = urlBuffer.toString();
try {
urls.addElement(new URL(url));
}
catch (MalformedURLException e) {
// skip this url
}
}
}
}
% java Weblogs shortlogs.xml
http://www.mozillazine.org
http://www.salonherringwiredfool.com/
http://www.slashdot.org/
SAX2 parsers--that is XMLReaders--are configured by features and properties
Feature and property names are absolute URIs
A feature is boolean, on or off, true or false; a property is an object
public boolean getFeature(String name)
throws SAXNotRecognizedException, SAXNotSupportedException
public void setFeature(String name, boolean value)
throws SAXNotRecognizedException, SAXNotSupportedException
public Object getProperty(String name)
throws SAXNotRecognizedException, SAXNotSupportedException
public void setProperty(String name, Object value)
throws SAXNotRecognizedException, SAXNotSupportedException
Features can be read-only or read/write.
Some features may be modifiable while parsing; others only before parsing starts
For example,
try {
if (xmlReader.getFeature("http://xml.org/sax/features/validation")) {
System.out.println("Parser is validating.");
}
else {
System.out.println("Parser is not validating.");
}
}
catch (SAXException e) {
System.out.println("Do not know if parser validates");
}
SAXNotRecognizedException
SAXNotSupportedException
http://xml.org/sax/features/namespaces
If true, then perform namespace processing.
If false, then, at parser option, do not perform namespace processing
access: (parsing) read-only; (not parsing) read/write
true by default
http://xml.org/sax/features/namespace-prefixes
If true, then report the original prefixed names and attributes used for namespace declarations.
If false, then do not report attributes used for namespace declarations, and optionally do not report original prefixed names.
false by default
access: (parsing) read-only; (not parsing) read/write
http://xml.org/sax/features/namespaces
http://xml.org/sax/features/namespace-prefixes
http://xml.org/sax/features/string-interning
If true, then all element names, prefixes, attribute
names, namespace URIs, and local names are internalized using
java.lang.String.intern()
.
If false, then names are not necessarily internalized.
access: (parsing) read-only; (not parsing) read/write
http://xml.org/sax/features/validation
If true, then report all validation errors
If false, then do not report validation errors.
access: (parsing) read-only; (not parsing) read/write
http://xml.org/sax/features/external-general-entities
If true, then include all external general (text) entities.
false: Do not include external general entities.
access: (parsing) read-only; (not parsing) read/write
http://xml.org/sax/features/external-parameter-entities
If true, then include all external parameter entities, including the external DTD subset.
false: Do not include any external parameter entities, even the external DTD subset.
access: (parsing) read-only; (not parsing) read/write
adapted from SAX2 documentation by David Megginson
Not all parsers are validating but Xerces-J is.
Validity errors are not fatal; therefore they do not throw SAXParseExceptions
Must install an ErrorHandler
as well as a
ContentHandler
Must set the feature http://xml.org/sax/features/validation
In increasing order of severity:
A warning; e.g. ambiguous content model, a constraint for compatibility
A recoverable error: typically a validity error
A fatal error: typically a well-formedness error
package org.xml.sax;
public interface ErrorHandler {
public void warning(SAXParseException exception)
throws SAXException;
public void error(SAXParseException exception)
throws SAXException;
public void fatalError(SAXParseException exception)
throws SAXException;
}
import org.xml.sax.*;
import java.io.*;
public class ValidityErrorReporter implements ErrorHandler {
private Writer out;
public ValidityErrorReporter(Writer out) {
this.out = out;
}
public ValidityErrorReporter() {
this(new OutputStreamWriter(System.out));
}
public void warning(SAXParseException ex)
throws SAXException {
try {
out.write(ex.getMessage() + "\r\n");
out.write(" at line " + ex.getLineNumber() + ", column "
+ ex.getColumnNumber() + "\r\n");
out.flush();
}
catch (IOException e) {
throw new SAXException(e);
}
}
public void error(SAXParseException ex)
throws SAXException {
try {
out.write(ex.getMessage() + "\r\n");
out.write(" at line " + ex.getLineNumber() + ", column "
+ ex.getColumnNumber() + "\r\n");
out.flush();
}
catch (IOException e) {
throw new SAXException(e);
}
}
public void fatalError(SAXParseException ex)
throws SAXException {
try {
out.write(ex.getMessage() + "\r\n");
out.write(" at line " + ex.getLineNumber() + ", column "
+ ex.getColumnNumber() + "\r\n");
out.flush();
}
catch (IOException e) {
throw new SAXException(e);
}
}
}
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.*;
import java.io.*;
public class SAX2Validator {
public static void main(String[] args) {
XMLReader parser;
try {
parser = XMLReaderFactory.createXMLReader();
}
catch (SAXException ex) {
try {
parser = XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser"
);
}
catch (SAXException ex2) {
System.err.println("Could not locate a SAX2 Parser");
return;
}
}
// turn on validation
try {
parser.setFeature(
"http://xml.org/sax/features/validation", true);
parser.setErrorHandler(new ValidityErrorReporter());
}
catch (SAXNotRecognizedException e) {
System.err.println(
"Installed XML parser cannot validate;"
+ " checking for well-formedness instead...");
}
catch (SAXNotSupportedException e) {
System.err.println(
"Cannot turn on validation here; "
+ "checking for well-formedness instead...");
}
if (args.length == 0) {
System.out.println("Usage: java SAX2Validator URL1 URL2...");
}
// start parsing...
for (int i = 0; i < args.length; i++) {
// command line should offer URIs or file names
try {
parser.parse(args[i]);
// If there are no well-formedness errors,
// then no exception is thrown
System.out.println(args[i] + " is well formed.");
}
catch (SAXParseException e) { // well-formedness error
System.out.println(args[i] + " is not well formed.");
System.out.println(e.getMessage()
+ " at line " + e.getLineNumber()
+ ", column " + e.getColumnNumber());
}
catch (SAXException e) { // some other kind of error
System.out.println(e.getMessage());
}
catch (IOException e) {
System.out.println("Could not check " + args[i]
+ " because of the IOException " + e);
}
}
}
}
http://xml.org/sax/properties/lexical-handler
data type:
org.xml.sax.ext.LexicalHandler
description: An optional extension handler for items like comments that are not part of the information set and may be omitted.
access: read/write
http://xml.org/sax/properties/declaration-handler
data type:
org.xml.sax.ext.DeclHandler
description: An optional extension handler for ATTLIST and ELEMENT declarations (but not notations and unparsed entities).
access: read/write
http://xml.org/sax/properties/dom-node
data type: org.w3c.dom.Node
description: When parsing, the current DOM node being visited if this is a DOM iterator; when not parsing, the root DOM node for iteration.
access: (parsing) read-only; (not parsing) read/write
http://xml.org/sax/properties/xml-string
data type: java.lang.String
description: The literal string of characters that was the source for the current event.
access: read-only
adapted from SAX2 documentation by David Megginson
http://apache.org/xml/features/validation/dynamic
True: The parser will validate the document
if a DTD is specified in a DOCTYPE
declaration or using the appropriate
schema attributes like xsi:noNamespaceSchemaLocation
.
False: Validation is determined by the state of the http://xml.org/sax/features/validation feature.
Default is false
http://apache.org/xml/features/validation/warn-on-duplicate-attdef
True: Warn on duplicate attribute declaration.
False: Do not warn on duplicate attribute declaration.
Default: true
http://apache.org/xml/features/validation/warn-on-undeclared-elemdef
True: Warn if element referenced in content model is not declared.
False: Do not warn if element referenced in content model is not declared.
Default: true
http://apache.org/xml/features/allow-java-encodings
True: Allow Java encoding names like 8859_1 in XML and text declarations.
False: Do not allow Java encoding names in XML and text declarations.
Default: false
http://apache.org/xml/features/continue-after-fatal-error
True: Continue after fatal error.
False: Stops parse on first fatal error.
Default: false
http://apache.org/xml/features/validation/schema
True: validate against a schema
False: do not use any schemas
Default: false
http://apache.org/xml/features/validation/schema-full-checking
True: perform checking that may be time-consuming or memory intensive.
False: skip some checks
Default: false
http://apache.org/xml/features/validation/schema/normalized-value
True: Normalize element and attribute values according to their schema type
False: don't normalize
Default: false
http://apache.org/xml/features/validation/schema/element-default
True: Provide schema element default values
False: don't report element default values
Default: true
http://apache.org/xml/properties/schema/external-schemaLocation
http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation
Extension handlers are non-required interfaces in the
org.xml.sax.ext
package.
To set the
LexicalHandler
for an XML reader, set the property
http://xml.org/sax/handlers/LexicalHandler
.
To set the
DeclHandler
for an XML reader, set the property
http://xml.org/sax/handlers/DeclHandler
.
If the reader does not support the requested property, it will throw a
SAXNotRecognizedException
or a SAXNotSupportedException
.
The startElement()
method in
ContentHandler
receives as an argument an
Attributes
object containing all attributes
on that tag.
public void startElement(String namespaceURI,
String localName, String qualifiedName, Attributes atts) throws SAXException
The Attributes
interface:
package org.xml.sax;
public interface Attributes {
public int getLength();
public String getURI(int index);
public String getLocalName(int index);
public String getQName(int index);
public String getType(int index);
public String getValue(int index);
public int getIndex(String uri, String localName);
public int getIndex(String qualifiedName);
public String getType(String uri, String localName);
public String getType(String qualifiedName);
public String getValue(String uri, String localName);
public String getValue(String qualifiedName);
}
import org.xml.sax.*;
import org.apache.xerces.parsers.*;
import java.io.*;
import java.util.*;
import org.xml.sax.helpers.*;
public class XLinkSpider extends DefaultHandler {
public static Enumeration listURIs(String systemId)
throws SAXException, IOException {
// set up the parser
XMLReader parser;
try {
parser = XMLReaderFactory.createXMLReader();
}
catch (SAXException e) {
try {
parser = XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser");
}
catch (SAXException e2) {
System.err.println("Error: could not locate a parser.");
return null;
}
}
// Install the Content Handler
XLinkSpider spider = new XLinkSpider();
parser.setContentHandler(spider);
parser.parse(systemId);
return spider.uris.elements();
}
private Vector uris = new Vector();
public void startElement(String namespaceURI, String localName,
String rawName, Attributes atts) throws SAXException {
String uri = atts.getValue(
"http://www.w3.org/1999/xlink", "href");
if (uri != null) uris.addElement(uri);
}
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Usage: java XLinkSpider URL1 URL2...");
}
// start parsing...
for (int i = 0; i < args.length; i++) {
try {
Enumeration uris = listURIs(args[i]);
while (uris.hasMoreElements()) {
String s = (String) uris.nextElement();
System.out.println(s);
}
}
catch (Exception e) {
System.err.println(e);
e.printStackTrace();
}
} // end for
} // end main
} // end XLinkSpider
The EntityResolver
allows you to substitute your own URI
lookup scheme for external entities
Especially useful for entities that use URL and URI schemes not supported by Java's protocol handlers; e.g. jdbc: or isbn:
The EntityResolver
interface:
package org.xml.sax;
import java.io.IOException;
public interface EntityResolver {
public InputSource resolveEntity (String publicID,
String systemID) throws SAXException, IOException;
}
import org.xml.sax.*;
public class RSSResolver implements EntityResolver {
public InputSource resolveEntity(String publicID, String systemID) {
if ( publicID.equals(
"-//Netscape Communications//DTD RSS 0.91//EN")
|| systemID.equals(
"http://my.netscape.com/publish/formats/rss-0.91.dtd")) {
return new InputSource(
"http://www.cafeconleche.org/dtds/rss.dtd");
}
else {
// use the default behaviour
return null;
}
}
}
Keeps track of namespace bindings on a stack
Allows you to determine what URI a prefix is mapped to at any point in the document
The NamespaceSupport
class:
package org.xml.sax.helpers;
public class NamespaceSupport {
public final static String XMLNS = "http://www.w3.org/XML/1998/namespace";
public NamespaceSupport();
public void reset();
public void pushContext();
public void popContext();
public boolean declarePrefix(String prefix, String uri);
public String getURI(String prefix);
public Enumeration getPrefixes();
public Enumeration getDeclaredPrefixes();
public String[] processName(String qualifiedName,
String[] parts, boolean isAttribute);
}
The XMLFilter
interface is like an XML reader,
"except that it obtains its events from another XML reader
rather than a primary source like an XML document or database.
Filters can modify a stream of
events as they pass on to the final application."
The parent is the parser the filter gets the data from.
Only two methods in the interface:
public void setParent(XMLReader parent)
public XMLReader getParent()
XMLFilterImpl
is a default filter that simply passes along
all events it receives:
public class XMLFilterImpl implements XMLFilter, EntityResolver, DTDHandler,
ContentHandler, ErrorHandler
Only new methods are constructors:
public XMLFilterImpl()
public XMLFilterImpl(XMLReader parent)
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.util.*;
import java.io.IOException;
public class UnparsedTextFilter extends XMLFilterImpl {
private TextEntityReplacer replacer;
public UnparsedTextFilter(XMLReader parent) {
super(parent);
}
public void parse(InputSource input)
throws IOException, SAXException {
System.out.println("parsing");
replacer = new TextEntityReplacer(input.getPublicId(),
input.getSystemId());
this.setDTDHandler(replacer);
this.setContentHandler(this);
}
// The other parse() method just calls this one
public void parse(String systemId)
throws IOException, SAXException {
parse(new InputSource(systemId));
}
public void startElement(String uri, String localName,
String qualifiedName, Attributes attributes) throws SAXException {
System.out.println("startElement");
Vector extraText = new Vector();
// Are there any unparsed entities in the attributes?
for (int i = 0; i < attributes.getLength(); i++) {
if (attributes.getType(i).equals("ENTITY")) {
try {
System.out.println("replacing");
String s = replacer.getText(attributes.getValue(i));
if (s != null) extraText.addElement(s);
}
catch (IOException e) {
System.err.println(e);
}
}
}
super.startElement(uri, localName, qualifiedName, attributes);
// Now spew out the values of the unparsed entities:
Enumeration e = extraText.elements();
while (e.hasMoreElements()) {
Object o = e.nextElement();
String s = (String) o;
super.characters(s.toCharArray(), 0, s.length());
}
}
}
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.util.*;
import java.io.IOException;
import org.apache.xml.serialize.*;
public class TextMerger {
public static void main(String[] args) {
XMLReader base;
try {
base = XMLReaderFactory.createXMLReader(
"org.apache.xerces.parsers.SAXParser");
}
catch (Exception e) {
// fall back on default parser
try {
base = XMLReaderFactory.createXMLReader();
}
catch (Exception ee) {
System.err.println("Couldn't locate a SAX parser");
return;
}
}
XMLReader parser = new UnparsedTextFilter(base);
//essentially a pretty printer
XMLSerializer printer
= new XMLSerializer(System.out, new OutputFormat());
base.setContentHandler(printer);
for (int i = 0; i < args.length; i++) {
try {
System.out.println("Parsing " + args[i]);
parser.parse(args[i]);
}
catch (SAXParseException e) { // well-formedness error
System.out.println(args[i] + " is not well formed.");
System.out.println(e.getMessage()
+ " at line " + e.getLineNumber()
+ ", column " + e.getColumnNumber());
}
catch (SAXException e) { // some other kind of error
System.out.println(e.getMessage());
}
catch (IOException e) {
System.out.println("Could not report on " + args[i]
+ " because of the IOException " + e);
}
} // end for
System.out.flush();
}
}
Encapsulates access to data so that it looks the same whether it's coming from a
URL
file
stream
reader
database
something else
Used in SAX1 and SAX2
Allows the source to be changed
package org.xml.sax;
import java.io.*;
public class InputSource {
public InputSource()
public InputSource(String systemID)
public InputSource(InputStream in)
public InputSource(Reader in)
public void setPublicId(String publicID)
public String getPublicId()
public void setSystemId(String systemID)
public String getSystemId()
public void setByteStream(InputStream byteStream)
public InputStream getByteStream()
public void setEncoding(String encoding)
public String getEncoding()
public void setCharacterStream(Reader characterStream)
public Reader getCharacterStream()
}
import org.xml.sax;
import java.io.*;
import java.net.*;
import java.util.zip.*;
...
try {
URL u = new URL(
"http://www.cafeconleche.org/examples/1998validstats.xml.gz");
InputStream raw = u.openStream();
InputStream decompressed = new GZIPInputStream(raw);
InputSource in = new InputSource(decompressed);
// read the document...
}
catch (IOException e) {
System.err.println(e);
}
catch (SAXException e) {
System.err.println(e);
}
ELEMENT, ATTLIST, ENTITY declarations are only optionally reported
Schema declarations aren't reported at all
Lexical events are only optionally reported
SAX2 can be configured on top of a lot of different parsers with different capabilities. What the parser does is more important than what SAX2 does.
You do not always have all the information you need at the time of a given callback
You may need to store information in various data structures (stacks, queues,vectors, arrays, etc.) and act on it at a later point
For example the characters()
method is not guaranteed
to give you the maximum number of contiguous characters. It may
split a single run of characters over multiple method calls.
Elliotte Rusty Harold
Addison Wesley, 2002
Chapters 6-8:
Chapter 6, SAX: http://www.cafeconleche.org/books/xmljava/chapters/ch06.html
Chapter 7, The XMLReader Interface: http://www.cafeconleche.org/books/xmljava/chapters/ch07.html
Chapter 8, SAX Filters: http://www.cafeconleche.org/books/xmljava/chapters/ch08.html
XML in a Nutshell, third edition
Elliotte Rusty Harold and W. Scott Means
O'Reilly & Associates, 2004
ISBN 0-596-00764-7
SAX website: http://www.saxproject.org/
The DOM (like XML) is not a triumph of elegance; it's a triumph of "if we do not hang together, we shall hang separately." At least the Browser Wars were not followed by API Wars. Better a common API that we all love to hate than a bazillion contending APIs that carve the Web up into contending enclaves of True Believers.
--Mike Champion on the xml-dev mailing list, Thursday, September 27, 2001
Writing with DOM
Reading with DOM
An XML document can be represented as a tree.
It has a root.
It has nodes.
It is amenable to recursive processing.
Not all applications agree on what the root is.
Not all applications agree on what is and isn't a node.
Defines how XML and HTML documents are represented as objects in programs
Defined in IDL; thus language independent
HTML as well as XML
Writing as well as reading
Covers everything except internal and external DTD subsets
DOM focuses more on the document; SAX focuses more on the parser.
DOM Level 0:
DOM Level 1, a W3C Standard
DOM Level 2, a W3C Standard
DOM Level 3: Several Working Drafts:
Document Object Model (DOM) Level 3 XPath Specification Version 1.0
Document Object Model (DOM) Level 3 Load and Save Specification
Document Object Model (DOM) Level 3 Validation Specification
Document Object Model (DOM) Level 3 Views and Formatting Specification
Document Object Model (DOM) Level 3 Events Specification Version 1.0
Apache XML Project's Xerces Java: http://xml.apache.org/xerces-j/index.html
IBM's XML for Java: http://www.alphaworks.ibm.com/formula/xml
Sun's Java API for XML http://java.sun.com/products/xml
GNU JAXP: http://www.gnu.org/software/classpathx/jaxp/jaxp.html
Eight Modules:
Core: org.w3c.dom
*
HTML: org.w3c.dom.html
Views: org.w3c.dom.views
StyleSheets: org.w3c.dom.stylesheets
CSS: org.w3c.dom.css
Events: org.w3c.dom.events
*
Traversal: org.w3c.dom.traversal
*
Range: org.w3c.dom.range
Only the core and traversal modules really apply to XML. The other six are for HTML.
* indicates Xerces support
Entire document is represented as a tree.
A tree contains nodes.
Some nodes may contain other nodes (depending on node type).
Each document node contains:
zero or one doctype nodes
one root element node
zero or more comment and processing instruction nodes
17 interfaces:
Attr
CDATASection
CharacterData
Comment
Document
DocumentFragment
DocumentType
DOMImplementation
Element
Entity
EntityReference
NamedNodeMap
Node
NodeList
Notation
ProcessingInstruction
Text
plus one exception:
DOMException
Plus a bunch of HTML stuff in org.w3c.dom.html
and other packages
we will ignore
Library specific code creates a parser
The parser parses the document and returns a DOM
org.w3c.dom.Document
object.
The entire document is stored in memory.
DOM methods and interfaces are used to extract data from this object
import org.apache.xerces.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;
public class DOMParserMaker {
public static void main(String[] args) {
// This is simpler but less flexible than the SAX approach.
// Perhaps a good creational design pattern is needed here?
DOMParser parser = new DOMParser();
for (int i = 0; i < args.length; i++) {
try {
// Read the entire document into memory
parser.parse(args[i]);
Document d = parser.getDocument();
// work with the document...
}
catch (SAXException e) {
System.err.println(e);
}
catch (IOException e) {
System.err.println(e);
}
}
}
}
javax.xml.parsers.DocumentBuilderFactory.newInstance()
creates a DocumentBuilderFactory
Configure the factory
The factory's newBuilder()
method
creates a DocumentBuilder
Configure the builder
The builder parses the document and returns a DOM
org.w3c.dom.Document
object.
The entire document is stored in memory.
DOM methods and interfaces are used to extract data from this object
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;
public class JAXPParserMaker {
public static void main(String[] args) {
try {
DocumentBuilderFactory builderFactory
= DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
DocumentBuilder parser
= builderFactory.newDocumentBuilder();
for (int i = 0; i < args.length; i++) {
try {
// Read the entire document into memory
Document d = parser.parse(args[i]);
// work with the document...
}
catch (SAXException e) {
System.err.println(e);
}
catch (IOException e) {
System.err.println(e);
}
} // end for
}
catch (ParserConfigurationException e) {
System.err.println("You need to install a JAXP aware parser.");
}
}
}
package org.w3c.dom;
public interface Node {
// NodeType
public static final short ELEMENT_NODE = 1;
public static final short ATTRIBUTE_NODE = 2;
public static final short TEXT_NODE = 3;
public static final short CDATA_SECTION_NODE = 4;
public static final short ENTITY_REFERENCE_NODE = 5;
public static final short ENTITY_NODE = 6;
public static final short PROCESSING_INSTRUCTION_NODE = 7;
public static final short COMMENT_NODE = 8;
public static final short DOCUMENT_NODE = 9;
public static final short DOCUMENT_TYPE_NODE = 10;
public static final short DOCUMENT_FRAGMENT_NODE = 11;
public static final short NOTATION_NODE = 12;
public String getNodeName();
public String getNodeValue() throws DOMException;
public void setNodeValue(String nodeValue) throws DOMException;
public short getNodeType();
public Node getParentNode();
public NodeList getChildNodes();
public Node getFirstChild();
public Node getLastChild();
public Node getPreviousSibling();
public Node getNextSibling();
public NamedNodeMap getAttributes();
public Document getOwnerDocument();
public Node insertBefore(Node newChild, Node refChild) throws DOMException;
public Node replaceChild(Node newChild, Node oldChild) throws DOMException;
public Node removeChild(Node oldChild) throws DOMException;
public Node appendChild(Node newChild) throws DOMException;
public boolean hasChildNodes();
public Node cloneNode(boolean deep);
public void normalize();
public boolean supports(String feature, String version);
public String getNamespaceURI();
public String getPrefix();
public void setPrefix(String prefix) throws DOMException;
public String getLocalName();
}
package org.w3c.dom;
public interface NodeList {
public Node item(int index);
public int getLength();
}
Now we're really ready to read a document
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;
public class NodeReporter {
public static void main(String[] args) {
try {
DocumentBuilderFactory builderFactory
= DocumentBuilderFactory.newInstance();
DocumentBuilder parser
= builderFactory.newDocumentBuilder();
NodeReporter iterator = new NodeReporter();
for (int i = 0; i < args.length; i++) {
try {
// Read the entire document into memory
Document doc = parser.parse(args[i]);
iterator.followNode(doc);
}
catch (SAXException ex) {
System.err.println(args[i] + " is not well-formed.");
}
catch (IOException ex) {
System.err.println(ex);
}
}
}
catch (ParserConfigurationException ex) {
System.err.println("You need to install a JAXP aware parser.");
}
} // end main
// note use of recursion
public void followNode(Node node) {
processNode(node);
if (node.hasChildNodes()) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
followNode(children.item(i));
}
}
}
public void processNode(Node node) {
String name = node.getNodeName();
String type = getTypeName(node.getNodeType());
System.out.println("Type " + type + ": " + name);
}
public static String getTypeName(int type) {
switch (type) {
case Node.ELEMENT_NODE:
return "Element";
case Node.ATTRIBUTE_NODE:
return "Attribute";
case Node.TEXT_NODE:
return "Text";
case Node.CDATA_SECTION_NODE:
return "CDATA Section";
case Node.ENTITY_REFERENCE_NODE:
return "Entity Reference";
case Node.ENTITY_NODE:
return "Entity";
case Node.PROCESSING_INSTRUCTION_NODE:
return "Processing Instruction";
case Node.COMMENT_NODE :
return "Comment";
case Node.DOCUMENT_NODE:
return "Document";
case Node.DOCUMENT_TYPE_NODE:
return "Document Type Declaration";
case Node.DOCUMENT_FRAGMENT_NODE:
return "Document Fragment";
case Node.NOTATION_NODE:
return "Notation";
default:
return "Unknown Type";
}
}
}
% java NodeReporter hotcop.xml Type Document: #document Type Processing Instruction: xml-stylesheet Type Document Type Declaration: SONG Type Element: SONG Type Text: #text Type Element: TITLE Type Text: #text Type Text: #text Type Element: PHOTO Type Text: #text Type Element: COMPOSER Type Text: #text Type Text: #text Type Element: COMPOSER Type Text: #text Type Text: #text Type Element: COMPOSER Type Text: #text Type Text: #text Type Element: PRODUCER Type Text: #text Type Text: #text Type Comment: #comment Type Text: #text Type Element: PUBLISHER Type Text: #text Type Text: #text Type Element: LENGTH Type Text: #text Type Text: #text Type Element: YEAR Type Text: #text Type Text: #text Type Element: ARTIST Type Text: #text Type Text: #text Type Comment: #comment
Attributes are missing from this output. They are not nodes. They are properties of nodes.
Node Type | Node Value |
---|---|
element node | null |
attribute node | attribute value |
text node | text of the node |
CDATA section node | text of the section |
entity reference node | null |
entity node | null |
processing instruction node | content of the processing instruction, not including the target |
comment node | text of the comment |
document node | null |
document type declaration node | null |
document fragment node | null |
notation node | null |
The root node representing the entire document; not the same as the root element
Contains:
one element node
zero or more processing instruction nodes
zero or more comment nodes
zero or one document type nodes
package org.w3c.dom;
public interface Document extends Node {
public DocumentType getDoctype();
public DOMImplementation getImplementation();
public Element getDocumentElement();
public NodeList getElementsByTagName(String tagname);
public NodeList getElementsByTagNameNS(String namespaceURI, String localName);
public Element getElementById(String elementId);
// Factory methods
public Element createElement(String tagName) throws DOMException;
public Element createElementNS(String namespaceURI, String qualifiedName) throws DOMException;
public DocumentFragment createDocumentFragment();
public Text createTextNode(String data);
public Comment createComment(String data);
public CDATASection createCDATASection(String data) throws DOMException;
public ProcessingInstruction createProcessingInstruction(String target, String data)
throws DOMException;
public Attr createAttribute(String name) throws DOMException;
public Attr createAttributeNS(String namespaceURI, String qualifiedName) throws DOMException;
public EntityReference createEntityReference(String name) throws DOMException;
public Node importNode(Node importedNode, boolean deep) throws DOMException;
}
UserLand's RSS based list of Web logs at http://static.userland.com/weblogMonitor/logs.xml:
<?xml version="1.0"?>
<!-- <!DOCTYPE foo SYSTEM "http://msdn.microsoft.com/xml/general/htmlentities.dtd"> -->
<weblogs>
<log>
<name>MozillaZine</name>
<url>http://www.mozillazine.org</url>
<changesUrl>http://www.mozillazine.org/contents.rdf</changesUrl>
<ownerName>Jason Kersey</ownerName>
<ownerEmail>kerz@en.com</ownerEmail>
<description>THE source for news on the Mozilla Organization. DevChats, Reviews, Chats, Builds, Demos, Screenshots, and more.</description>
<imageUrl></imageUrl>
<adImageUrl>http://static.userland.com/weblogMonitor/ads/kerz@en.com.gif</adImageUrl>
</log>
<log>
<name>SalonHerringWiredFool</name>
<url>http://www.salonherringwiredfool.com/</url>
<ownerName>Some Random Herring</ownerName>
<ownerEmail>salonfool@wiredherring.com</ownerEmail>
<description></description>
</log>
<log>
<name>Scripting News</name>
<url>http://www.scripting.com/</url>
<ownerName>Dave Winer</ownerName>
<ownerEmail>dave@userland.com</ownerEmail>
<description>News and commentary from the cross-platform scripting community.</description>
<imageUrl>http://www.scripting.com/gifs/tinyScriptingNews.gif</imageUrl>
<adImageUrl>http://static.userland.com/weblogMonitor/ads/dave@userland.com.gif</adImageUrl>
</log>
<log>
<name>SlashDot.Org</name>
<url>http://www.slashdot.org/</url>
<ownerName>Simply a friend</ownerName>
<ownerEmail>afriendofweblogs@weblogs.com</ownerEmail>
<description>News for Nerds, Stuff that Matters.</description>
</log>
</weblogs>
We can easily find out how many URLs there will be when we finish parsing, since they're all in memory.
Single threaded by nature; no benefit to multiple threads since no data will be available until the entire document has been read and parsed.
The character data of each url
element needs to be read.
Everything else can be ignored.
The getElementsByTagName()
method in
Document
gives us a quick list of all the
url
elements.
The XML parsing is so straight-forward it can be done inside one method. No extra class is required.
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.util.*;
import java.net.*;
public class WeblogsDOM {
public static String DEFAULT_URL
= "http://static.userland.com/weblogMonitor/logs.xml";
public static List listChannels() throws DOMException {
return listChannels(DEFAULT_URL);
}
public static List listChannels(String uri) throws DOMException {
if (uri == null) {
throw new NullPointerException("URL must be non-null");
}
org.apache.xerces.parsers.DOMParser parser
= new org.apache.xerces.parsers.DOMParser();
Vector urls = null;
try {
// Read the entire document into memory
parser.parse(uri);
Document doc = parser.getDocument();
NodeList logs = doc.getElementsByTagName("url");
urls = new Vector(logs.getLength());
for (int i = 0; i < logs.getLength(); i++) {
try {
Node element = logs.item(i);
Node text = element.getFirstChild();
String content = text.getNodeValue();
URL u = new URL(content);
urls.addElement(u);
}
catch (MalformedURLException e) {
// bad input data from one third party; just ignore it
}
}
}
catch (SAXException e) {
System.err.println(e);
}
catch (IOException e) {
System.err.println(e);
}
return urls;
}
public static void main(String[] args) {
try {
List urls;
if (args.length > 0) {
try {
URL url = new URL(args[0]);
urls = listChannels(args[0]);
}
catch (MalformedURLException e) {
System.err.println("Usage: java WeblogsDOM url");
return;
}
}
else {
urls = listChannels();
}
Iterator iterator = urls.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
catch (/* Unexpected */ Exception e) {
e.printStackTrace();
}
} // end main
}
% java WeblogsDOM
http://2020Hindsight.editthispage.com/
http://www.sff.net/people/mitchw/weblog/weblog.htp
http://nate.weblogs.com/
http://plugins.launchpoint.net
http://404.psistorm.net
http://home.att.net/~geek9000
http://daubnet.tzo.com/weblog
several hundred more...
Represents a complete element including its start-tag, end-tag, and content
Contains:
Element nodes
ProcessingInstruction nodes
Comment nodes
Text nodes
CDATASection nodes
EntityReference nodes
package org.w3c.dom;
public interface Element extends Node {
public String getTagName();
public NodeList getElementsByTagName(String name);
public NodeList getElementsByTagNameNS(String namespaceURI,
String localName);
public String getAttribute(String name);
public String getAttributeNS(String namespaceURI,
String localName);
public void setAttribute(String name, String value)
throws DOMException;
public void setAttributeNS(String namespaceURI,
String qualifiedName, String value) throws DOMException;
public void removeAttribute(String name) throws DOMException;
public void removeAttributeNS(String namespaceURI,
String localName) throws DOMException;
public Attr getAttributeNode(String name);
public Attr getAttributeNodeNS(String namespaceURI, String localName);
public Attr setAttributeNode(Attr newAttr) throws DOMException;
public Attr setAttributeNodeNS(Attr newAttr) throws DOMException;
public Attr removeAttributeNode(Attr oldAttr) throws DOMException;
}
import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.IOException;
import org.apache.xml.serialize.*;
public class IDTagger {
int id = 1;
public void processNode(Node node) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
String currentID = element.getAttribute("ID");
if (currentID == null || currentID.equals("")) {
element.setAttribute("ID", "_" + id);
id = id + 1;
}
}
}
// note use of recursion
public void followNode(Node node) {
processNode(node);
if (node.hasChildNodes()) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
followNode(children.item(i));
}
}
}
public static void main(String[] args) {
DOMParser parser = new DOMParser();
IDTagger iterator = new IDTagger();
for (int i = 0; i < args.length; i++) {
try {
// Read the entire document into memory
parser.parse(args[i]);
Document document = parser.getDocument();
iterator.followNode(document);
// now we serialize the document...
OutputFormat format = new OutputFormat(document);
XMLSerializer serializer
= new XMLSerializer(System.out, format);
serializer.serialize(document);
}
catch (SAXException e) {
System.err.println(e);
}
catch (IOException e) {
System.err.println(e);
}
}
} // end main
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE SONG SYSTEM "song.dtd">
<?xml-stylesheet type="text/css" href="song.css"?><!-- This should be a four digit year like "1999",
not a two-digit year like "99" --><SONG xmlns="http://www.cafeconleche.org/namespace/song" ID="_1" xmlns:xlink="http://www.w3.org/1999/xlink"> <TITLE ID="_2">Hot Cop</TITLE> <PHOTO ALT="Victor Willis in Cop Outfit" HEIGHT="200" ID="_3" WIDTH="100" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="hotcop.jpg" xlink:show="onLoad" xlink:type="simple"/> <COMPOSER ID="_4">Jacques Morali</COMPOSER> <COMPOSER ID="_5">Henri Belolo</COMPOSER> <COMPOSER ID="_6">Victor Willis</COMPOSER> <PRODUCER ID="_7">Jacques Morali</PRODUCER> <!-- The publisher is actually Polygram but I needed
an example of a general entity reference. --> <PUBLISHER ID="_8" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.amrecords.com/" xlink:type="simple"> A & M Records </PUBLISHER> <LENGTH ID="_9">6:20</LENGTH> <YEAR ID="_10">1978</YEAR> <ARTIST ID="_11">Village People</ARTIST> </SONG><!-- You can tell what album I was
listening to when I wrote this example -->
View Output in BrowserRepresents things that are basically text holders
Super interface of Text
, Comment
,
and CDATASection
package org.w3c.dom;
public interface CharacterData extends Node {
public String getData() throws DOMException;
public void setData(String data) throws DOMException;
public int getLength();
public String substringData(int offset, int count)
throws DOMException;
public void appendData(String arg)
throws DOMException;
public void insertData(int offset, String arg)
throws DOMException;
public void deleteData(int offset, int count)
throws DOMException;
public void replaceData(int offset, int count, String arg)
throws DOMException;
}
import org.apache.xerces.parsers.DOMParser;
import org.apache.xml.serialize.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.IOException;
public class ROT13XML {
public void processNode(Node node) {
if (node.getNodeType() == Node.TEXT_NODE
|| node.getNodeType() == Node.COMMENT_NODE
|| node.getNodeType() == Node.CDATA_SECTION_NODE) {
CharacterData text = (CharacterData) node;
String data = text.getData();
text.setData(rot13(data));
}
}
// note use of recursion
public void followNode(Node node) {
processNode(node);
if (node.hasChildNodes()) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
followNode(children.item(i));
}
}
}
public static String rot13(String s) {
StringBuffer result = new StringBuffer(s.length());
for (int i = 0; i < s.length(); i++) {
int c = s.charAt(i);
if (c >= 'A' && c <= 'M') result.append((char) (c+13));
else if (c >= 'N' && c <= 'Z') result.append((char) (c-13));
else if (c >= 'a' && c <= 'm') result.append((char) (c+13));
else if (c >= 'n' && c <= 'z') result.append((char) (c-13));
else result.append((char) c);
}
return result.toString();
}
public static void main(String[] args) {
DOMParser parser = new DOMParser();
ROT13XML iterator = new ROT13XML();
for (int i = 0; i < args.length; i++) {
try {
// Read the entire document into memory
parser.parse(args[i]);
Document document = parser.getDocument();
iterator.followNode(document);
// now we serialize the document...
OutputFormat format = new OutputFormat(document);
XMLSerializer serializer
= new XMLSerializer(System.out, format);
serializer.serialize(document);
}
catch (SAXException e) {
System.err.println(e);
}
catch (IOException e) {
System.err.println(e);
}
}
} // end main
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE SONG SYSTEM "song.dtd">
<?xml-stylesheet type="text/css" href="song.css"?>
<SONG xmlns="http://metalab.unc.edu/xml/namespace/song"
xmlns:xlink="http://www.w3.org/1999/xlink"> <TITLE>Ubg Pbc</TITLE>
<PHOTO ALT="Victor Willis in Cop Outfit" HEIGHT="200" WIDTH="100"
xlink:href="hotcop.jpg" xlink:show="onLoad" xlink:type="simple"/>
<COMPOSER>Wnpdhrf Zbenyv</COMPOSER> <COMPOSER>Uraev Orybyb</COMPOSER>
<COMPOSER>Ivpgbe Jvyyvf</COMPOSER> <PRODUCER>Wnpdhrf Zbenyv</PRODUCER>
<!-- Gur choyvfure vf npghnyyl Cbyltenz ohg V arrqrq na rknzcyr
bs n trareny ragvgl ersrerapr. --> <PUBLISHER
xlink:href="http://www.amrecords.com/" xlink:type="simple"> N &
Z Erpbeqf </PUBLISHER> <LENGTH>6:20</LENGTH> <YEAR>1978</YEAR>
<ARTIST>Ivyyntr Crbcyr</ARTIST> </SONG>
<!-- Lbh pna gryy jung nyohz V jnf
yvfgravat gb jura V jebgr guvf rknzcyr -->
Represents the text content of an element or attribute
Contains only pure text, no markup
Parsers will return a single maximal text node for each contiguous run of pure text
Editing may change this
package org.w3c.dom;
public interface Text extends CharacterData {
public Text splitText(int offset) throws DOMException;
}
Represents a CDATA section like this example from a hypothetical SVG tutorial:
<p>You can use a default <code>xmlns</code> attribute to avoid
having to add the svg prefix to all your elements:</p>
<![CDATA[
<svg xmlns="http://www.w3.org/2000/svg"
width="12cm" height="10cm">
<ellipse rx="110" ry="130" />
<rect x="4cm" y="1cm" width="3cm" height="6cm" />
</svg>
]]>
No children
package org.w3c.dom;
public interface CDATASection extends Text {
}
Represents a document type declaration
Has no children
package org.w3c.dom;
public interface DocumentType extends Node {
public String getName();
public NamedNodeMap getEntities();
public NamedNodeMap getNotations();
public String getPublicId();
public String getSystemId();
public String getInternalSubset();
}
Verify that a document is correct XHTML
From the XHTML 1.0 spec:
It must validate against one of the three DTDs found in Appendix A.
The root element of the document must be
<html>
.
The root element of the document must designate the XHTML namespace using the
xmlns
attribute [XMLNAMES]. The namespace for XHTML is defined to behttp://www.w3.org/1999/xhtml
.
There must be a DOCTYPE declaration in the document prior to the root element. The public identifier included in the DOCTYPE declaration must reference one of the three DTDs found in Appendix A using the respective Formal Public Identifier. The system identifier may be changed to reflect local system conventions.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "DTD/xhtml1-frameset.dtd">
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.*;
import org.xml.sax.*;
public class XHTMLValidator {
public static void main(String[] args) {
if (args.length == 0) {
System.err.println("Usage: java XHTMLValidator URL");
return;
}
try {
DocumentBuilderFactory builderFactory
= DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
builderFactory.setValidating(true);
DocumentBuilder parser
= builderFactory.newDocumentBuilder();
parser.setErrorHandler(new ValidityErrorReporter());
Document document;
try {
document = parser.parse(args[0]);
// ValidityErrorReporter prints any validity errors detected
}
catch (SAXException e) {
System.out.println(args[0] + " is not valid.");
return;
}
// If we get this far, then the document is valid XML.
// Check to see whether the document is actually XHTML
DocumentType doctype = document.getDoctype();
if (doctype == null) {
System.out.println("No DOCTYPE");
return;
}
String name = doctype.getName();
String systemID = doctype.getSystemId();
String publicID = doctype.getPublicId();
if (!name.equals("html")) {
System.out.println("Incorrect root element name " + name);
}
if (publicID == null
|| (!publicID.equals("-//W3C//DTD XHTML 1.0 Strict//EN")
&& !publicID.equals(
"-//W3C//DTD XHTML 1.0 Transitional//EN")
&& !publicID.equals(
"-//W3C//DTD XHTML 1.0 Frameset//EN"))) {
System.out.println(args[0]
+ " does not seem to use an XHTML 1.0 DTD");
}
// Check the namespace on the root element
Element root = document.getDocumentElement();
String xmlnsValue = root.getAttribute("xmlns");
if (!xmlnsValue.equals("http://www.w3.org/1999/xhtml")) {
System.out.println(args[0]
+ " does not properly declare the"
+ " http://www.w3.org/1999/xhtml"
+ " namespace on the root element");
return;
}
System.out.println(args[0] + " is valid XHTML.");
}
catch (IOException e) {
System.err.println("Could not read " + args[0]);
}
catch (Exception e) {
System.err.println(e);
e.printStackTrace();
}
}
}
Represents an attribute
Contains:
Text nodes
Entity reference nodes
package org.w3c.dom;
public interface Attr extends Node {
public String getName();
public boolean getSpecified();
public String getValue();
public void setValue(String value) throws DOMException;
public Element getOwnerElement();
}
import org.xml.sax.*;
import java.io.*;
import java.util.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
public class DOMSpider {
private static DocumentBuilder parser;
// namespace support is turned off by default in JAXP
static {
try {
DocumentBuilderFactory builderFactory
= DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
parser = builderFactory.newDocumentBuilder();
}
catch (Exception ex) {
throw new RuntimeException("Couldn't build a parser!");
}
}
private static Vector visited = new Vector();
private static int maxDepth = 5;
private static int currentDepth = 0;
public static void listURIs(String systemId) {
currentDepth++;
try {
if (currentDepth < maxDepth) {
Document document = parser.parse(systemId);
Vector uris = new Vector();
// search the document for uris,
// store them in vector, and print them
searchForURIs(document.getDocumentElement(), uris);
Enumeration e = uris.elements();
while (e.hasMoreElements()) {
String uri = (String) e.nextElement();
visited.addElement(uri);
listURIs(uri);
}
}
}
catch (SAXException e) {
// couldn't load the document,
// probably not well-formed XML, skip it
}
catch (IOException e) {
// couldn't load the document,
// likely network failure, skip it
}
finally {
currentDepth--;
System.out.flush();
}
}
// use recursion
public static void searchForURIs(Element element, Vector uris) {
// look for XLinks in this element
String uri = element.getAttributeNS("http://www.w3.org/1999/xlink", "href");
if (uri != null && !uri.equals("")
&& !visited.contains(uri)
&& !uris.contains(uri)) {
System.out.println(uri);
uris.addElement(uri);
}
// process child elements recursively
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node n = children.item(i);
if (n instanceof Element) {
searchForURIs((Element) n, uris);
}
}
}
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Usage: java DOMSpider URL1 URL2...");
}
// start parsing...
for (int i = 0; i < args.length; i++) {
try {
listURIs(args[i]);
}
catch (Exception e) {
System.err.println(e);
e.printStackTrace();
}
} // end for
} // end main
} // end DOMSpider
Represents a processing instruction like
<?robots index="yes" follow="no"?>
No children
package org.w3c.dom;
public interface ProcessingInstruction extends Node {
public String getTarget();
public String getData();
public void setData(String data) throws DOMException;
}
import org.xml.sax.*;
import java.io.*;
import java.util.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
public class PoliteDOMSpider {
private static DocumentBuilder parser;
// namespace support is turned off by default in JAXP
static {
try {
DocumentBuilderFactory builderFactory
= DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
parser = builderFactory.newDocumentBuilder();
}
catch (Exception ex) {
throw new RuntimeException("Couldn't build a parser!");
}
}
private static Vector visited = new Vector();
private static int maxDepth = 5;
private static int currentDepth = 0;
public static boolean robotsAllowed(Document document) {
NodeList children = document.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node n = children.item(i);
if (n instanceof ProcessingInstruction) {
ProcessingInstruction pi = (ProcessingInstruction) n;
if (pi.getTarget().equals("robots")) {
String data = pi.getData();
if (data.indexOf("follow=\"no\"") >= 0) {
return false;
}
}
}
}
return true;
}
public static void listURIs(String systemId) {
currentDepth++;
try {
if (currentDepth < maxDepth) {
Document document = parser.parse(systemId);
if (robotsAllowed(document)) {
Vector uris = new Vector();
// search the document for uris,
// store them in vector, print them
searchForURIs(document.getDocumentElement(), uris);
Enumeration e = uris.elements();
while (e.hasMoreElements()) {
String uri = (String) e.nextElement();
visited.addElement(uri);
listURIs(uri);
}
}
}
}
catch (SAXException e) {
// couldn't load the document,
// probably not well-formed XML, skip it
}
catch (IOException e) {
// couldn't load the document,
// likely network failure, skip it
}
finally {
currentDepth--;
System.out.flush();
}
}
// use recursion
public static void searchForURIs(Element element, Vector uris) {
// look for XLinks in this element
String uri = element.getAttributeNS("http://www.w3.org/1999/xlink", "href");
if (uri != null && !uri.equals("")
&& !visited.contains(uri)
&& !uris.contains(uri)) {
System.out.println(uri);
uris.addElement(uri);
}
// process child elements recursively
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node n = children.item(i);
if (n instanceof Element) {
searchForURIs((Element) n, uris);
}
}
}
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Usage: java PoliteDOMSpider URL1 URL2...");
}
// start parsing...
for (int i = 0; i < args.length; i++) {
try {
listURIs(args[i]);
}
catch (Exception e) {
System.err.println(e);
e.printStackTrace();
}
} // end for
} // end main
} // end PoliteDOMSpider
Represents a comment like this example from the XML 1.0 spec:
<!--* N.B. some readers (notably JC) find the following
paragraph awkward and redundant. I agree it's logically redundant:
it *says* it is summarizing the logical implications of
matching the grammar, and that means by definition it's
logically redundant. I don't think it's rhetorically
redundant or unnecessary, though, so I'm keeping it. It
could however use some recasting when the editors are feeling
stronger. -MSM *-->
No children
package org.w3c.dom;
public interface Comment extends CharacterData {
}
import org.apache.xerces.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;
public class DOMCommentReader {
public static void main(String[] args) {
DOMParser parser = new DOMParser();
for (int i = 0; i < args.length; i++) {
try {
// Read the entire document into memory
parser.parse(args[i]);
Document d = parser.getDocument();
processNode(d);
}
catch (SAXException e) {
System.err.println(e);
}
catch (IOException e) {
System.err.println(e);
}
}
} // end main
// note use of recursion
public static void processNode(Node node) {
int type = node.getNodeType();
if (type == Node.COMMENT_NODE) {
System.out.println(node.getNodeValue());
System.out.println();
}
else {
if (node.hasChildNodes()) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
processNode(children.item(i));
}
}
}
}
}
% java DOMCommentReader hotcop.xml
The publisher is actually Polygram but I needed
an example of a general entity reference.
You can tell what album I was
listening to when I wrote this example
Or try http://www.w3.org/TR/1998/REC-xml-19980210.xml for more interesting output
A runtime exception but you should catch it
Error code gives more detailed information:
DOMException.INDEX_SIZE_ERR
DOMException.DOMSTRING_SIZE_ERR
String
DOMException.HIERARCHY_REQUEST_ERR
DOMException.WRONG_DOCUMENT_ERR
DOMException.INVALID_CHARACTER_ERR
DOMException.NO_DATA_ALLOWED_ERR
DOMException.NO_MODIFICATION_ALLOWED_ERR
DOMException.NOT_FOUND_ERR
DOMException.NOT_SUPPORTED_ERR
DOMException.INUSE_ATTRIBUTE_ERR
DOMException.INVALID_STATE_ERR
DOMException.SYNTAX_ERR
DOMException.INVALID_MODIFICATION_ERR
DOMException.NAMESPACE_ERR
DOMException.INVALID_ACCESS_ERR
Current value accessible from the public code
field
Four interfaces:
DocumentTraversal
NodeFilter
NodeIterator
TreeWalker
package org.w3c.dom.traversal;
public interface NodeIterator {
public int getWhatToShow();
public NodeFilter getFilter();
public boolean getExpandEntityReferences();
public Node nextNode() throws DOMException;
public Node previousNode() throws DOMException;
public void detach();
}
import org.apache.xerces.parsers.*;
import org.apache.xerces.dom.*;
import org.w3c.dom.*;
import org.w3c.dom.traversal.*;
import org.xml.sax.*;
import java.io.*;
public class ValueReporter {
public static void main(String[] args) {
DOMParser parser = new DOMParser();
for (int i = 0; i < args.length; i++) {
try {
// Read the entire document into memory
parser.parse(args[i]);
Document doc = parser.getDocument();
DocumentImpl impl = (DocumentImpl) doc;
NodeIterator iterator = impl.createNodeIterator(//filter
doc.getDocumentElement(), NodeFilter.SHOW_ALL, null
);
Node node;
while ((node = iterator.nextNode()) != null) {
processNode(node);
}
}
catch (SAXException e) {
System.err.println(e);
}
catch (IOException e) {
System.err.println(e);
}
}
} // end main
public static void processNode(Node node) {
String name = node.getNodeName();
String type = getTypeName(node.getNodeType());
String value = node.getNodeValue();
System.out.println("Type " + type + ": " + name
+ " \"" + value + "\"");
}
public static String getTypeName(int type) {
switch (type) {
case Node.ELEMENT_NODE:
return "Element";
case Node.ATTRIBUTE_NODE:
return "Attribute";
case Node.TEXT_NODE:
return "Text";
case Node.CDATA_SECTION_NODE:
return "CDATA Section";
case Node.ENTITY_REFERENCE_NODE:
return "Entity Reference";
case Node.ENTITY_NODE:
return "Entity";
case Node.PROCESSING_INSTRUCTION_NODE:
return "Processing Instruction";
case Node.COMMENT_NODE:
return "Comment";
case Node.DOCUMENT_NODE:
return "Document";
case Node.DOCUMENT_TYPE_NODE:
return "Document Type Declaration";
case Node.DOCUMENT_FRAGMENT_NODE:
return "Document Fragment";
case Node.NOTATION_NODE:
return "Notation";
default:
return "Unknown Type";
}
}
}
% java ValueReporter hotcop.xml Type Element: SONG "null" Type Text: #text " " Type Element: TITLE "null" Type Text: #text "Hot Cop" Type Text: #text " " Type Element: PHOTO "null" Type Text: #text " " Type Element: COMPOSER "null" Type Text: #text "Jacques Morali" Type Text: #text " " Type Element: COMPOSER "null" Type Text: #text "Henri Belolo" Type Text: #text " " Type Element: COMPOSER "null" Type Text: #text "Victor Willis" Type Text: #text " " Type Element: PRODUCER "null" Type Text: #text "Jacques Morali" Type Text: #text " " Type Comment: #comment " The publisher is actually Polygram but I needed an example of a general entity reference. " Type Text: #text " " Type Element: PUBLISHER "null" Type Text: #text " A & M Records " Type Text: #text " " Type Element: LENGTH "null" Type Text: #text "6:20" Type Text: #text " " Type Element: YEAR "null" Type Text: #text "1978" Type Text: #text " " Type Element: ARTIST "null" Type Text: #text "Village People" Type Text: #text " "
Attributes are missing from this output. They are not children. They are properties of nodes.
package org.w3c.dom.traversal;
public interface NodeFilter {
// Constants returned by acceptNode
public static final short FILTER_ACCEPT = 1;
public static final short FILTER_REJECT = 2;
public static final short FILTER_SKIP = 3;
// Constants for whatToShow
public static final int SHOW_ALL = 0x0000FFFF;
public static final int SHOW_ELEMENT = 0x00000001;
public static final int SHOW_ATTRIBUTE = 0x00000002;
public static final int SHOW_TEXT = 0x00000004;
public static final int SHOW_CDATA_SECTION = 0x00000008;
public static final int SHOW_ENTITY_REFERENCE = 0x00000010;
public static final int SHOW_ENTITY = 0x00000020;
public static final int SHOW_PROCESSING_INSTRUCTION = 0x00000040;
public static final int SHOW_COMMENT = 0x00000080;
public static final int SHOW_DOCUMENT = 0x00000100;
public static final int SHOW_DOCUMENT_TYPE = 0x00000200;
public static final int SHOW_DOCUMENT_FRAGMENT = 0x00000400;
public static final int SHOW_NOTATION = 0x00000800;
public short acceptNode(Node n);
}
import org.apache.xerces.parsers.*;
import org.apache.xerces.dom.*;
import org.w3c.dom.*;
import org.w3c.dom.traversal.*;
import org.xml.sax.SAXException;
import java.io.IOException;
public class DOMTagStripper {
public static void main(String[] args) {
DOMParser parser = new DOMParser();
for (int i = 0; i < args.length; i++) {
try {
// Read the entire document into memory
parser.parse(args[i]);
Document doc = parser.getDocument();
DocumentImpl impl = (DocumentImpl) doc;
NodeIterator iterator = impl.createNodeIterator(
doc.getDocumentElement(), NodeFilter.SHOW_TEXT, null, true
);
Node node;
while ((node = iterator.nextNode()) != null) {
System.out.print(node.getNodeValue());
}
}
catch (SAXException e) {
System.err.println(e);
}
catch (IOException e) {
System.err.println(e);
}
}
} // end main
}
% java DOMTagStripper hotcop.xml Hot Cop Jacques Morali Henri Belolo Victor Willis Jacques Morali A & M Records 6:20 1978 Village People
package org.w3c.dom.traversal;
public interface TreeWalker {
public int getWhatToShow();
public NodeFilter getFilter();
public boolean getExpandEntityReferences();
public Node getCurrentNode();
public void setCurrentNode(Node currentNode) throws DOMException;
public Node parentNode();
public Node firstChild();
public Node lastChild();
public Node previousSibling();
public Node nextSibling();
public Node previousNode();
public Node nextNode();
}
DOM is for both input and output
New documents are created with a parser-specific API or JAXP
A serializer + output format converts the DOM to a byte stream
Creates new Document objects
Creates new DocType objects
Tests features supported by this implementation
package org.w3c.dom;
public interface DOMImplementation {
public boolean hasFeature(String feature, String version)
public DocumentType createDocumentType(String qualifiedName,
String publicID, String systemID, String internalSubset)
public Document createDocument(String namespaceURI,
String qualifiedName, DocumentType doctype)
throws DOMException
}
The Xerces-specific class that implements DOMImplementation
package org.apache.xerces.dom;
public class DOMImplementationImpl implements DOMImplementation {
public boolean hasFeature(String feature, String version)
public static DOMImplementation getDOMImplementation()
public DocumentType createDocumentType(String qualifiedName,
String publicID, String systemID, String internalSubset)
public Document createDocument(String namespaceURI,
String qualifiedName, DocumentType doctype)
throws DOMException
}
import java.math.BigInteger;
import java.io.*;
import org.w3c.dom.*;
import org.apache.xerces.dom.*;
public class FibonacciDOM {
public static void main(String[] args) {
try {
DOMImplementation impl
= DOMImplementationImpl.getDOMImplementation();
Document fibonacci
= impl.createDocument(null, "Fibonacci_Numbers", null);
BigInteger low = BigInteger.ONE;
BigInteger high = BigInteger.ONE;
Element root = fibonacci.getDocumentElement();
for (int i = 1; i <= 25; i++) {
Element number = fibonacci.createElement("fibonacci");
number.setAttribute("index", Integer.toString(i));
Text text = fibonacci.createTextNode(low.toString());
number.appendChild(text);
root.appendChild(number);
BigInteger temp = high;
high = high.add(low);
low = temp;
}
// Now the document has been created and exists in memory
}
catch (DOMException e) {
e.printStackTrace();
}
}
}
import java.math.BigInteger;
import java.io.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
public class FibonacciJAXP {
public static void main(String[] args) {
try {
DocumentBuilderFactory factory
= DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
DOMImplementation impl = builder.getDOMImplementation();
Document fibonacci
= impl.createDocument(null, "Fibonacci_Numbers", null);
BigInteger low = BigInteger.ONE;
BigInteger high = BigInteger.ONE;
Element root = fibonacci.getDocumentElement();
for (int i = 1; i <= 25; i++) {
Element number = fibonacci.createElement("fibonacci");
number.setAttribute("index", Integer.toString(i));
Text text = fibonacci.createTextNode(low.toString());
number.appendChild(text);
root.appendChild(number);
BigInteger temp = high;
high = high.add(low);
low = temp;
}
// Now the document has been created and exists in memory
}
catch (DOMException e) {
e.printStackTrace();
}
catch (ParserConfigurationException e) {
System.err.println("You need to install a JAXP aware DOM implementation.");
}
}
}
The process of taking an in-memory DOM tree and converting it to a stream of characters that can be written onto an output stream
Not a standard part of DOM Level 2
The public interface DOMSerializer public interface Serializer public abstract class BaseMarkupSerializer
extends Object
implements DocumentHandler, org.xml.sax.misc.LexicalHandler, DTDHandler,
org.xml.sax.misc.DeclHandler, DOMSerializer, Serializer public class HTMLSerializer
extends BaseMarkupSerializer public final class TextSerializer
extends BaseMarkupSerializer public final class XHTMLSerializer
extends HTMLSerializer public final class XMLSerializer
extends BaseMarkupSerializerorg.apache.xml.serialize
package:
import java.math.BigInteger;
import java.io.*;
import org.w3c.dom.*;
import org.apache.xerces.dom.*;
import org.apache.xml.serialize.*;
public class FibonacciDOMSerializer {
public static void main(String[] args) {
try {
DOMImplementation impl
= DOMImplementationImpl.getDOMImplementation();
Document fibonacci
= impl.createDocument(null, "Fibonacci_Numbers", null);
BigInteger low = BigInteger.ONE;
BigInteger high = BigInteger.ONE;
Element root = fibonacci.getDocumentElement();
for (int i = 1; i <= 25; i++) {
Element number = fibonacci.createElement("fibonacci");
number.setAttribute("index", Integer.toString(i));
Text text = fibonacci.createTextNode(low.toString());
number.appendChild(text);
root.appendChild(number);
BigInteger temp = high;
high = high.add(low);
low = temp;
}
try {
// Now that the document is created we need to *serialize* it
OutputFormat format = new OutputFormat(fibonacci);
XMLSerializer serializer
= new XMLSerializer(System.out, format);
serializer.serialize(fibonacci);
}
catch (IOException e) {
System.err.println(e);
}
}
catch (DOMException e) {
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<Fibonacci_Numbers><fibonacci index="0">0</fibonacci><fibonacci index="1">1</fibonacci><fibonacci index="2">1</fibonacci><fibonacci index="3">2</fibonacci><fibonacci index="4">3</fibonacci><fibonacci index="5">5</fibonacci><fibonacci index="6">8</fibonacci><fibonacci index="7">13</fibonacci><fibonacci index="8">21</fibonacci><fibonacci index="9">34</fibonacci><fibonacci index="10">55</fibonacci><fibonacci index="11">89</fibonacci><fibonacci index="12">144</fibonacci><fibonacci index="13">233</fibonacci><fibonacci index="14">377</fibonacci><fibonacci index="15">610</fibonacci><fibonacci index="16">987</fibonacci><fibonacci index="17">1597</fibonacci><fibonacci index="18">2584</fibonacci><fibonacci index="19">4181</fibonacci><fibonacci index="20">6765</fibonacci><fibonacci index="21">10946</fibonacci><fibonacci index="22">17711</fibonacci><fibonacci index="23">28657</fibonacci><fibonacci index="24">46368</fibonacci><fibonacci index="25">75025</fibonacci></Fibonacci_Numbers>
package org.apache.xml.serialize;
public class OutputFormat extends Object {
public OutputFormat()
public OutputFormat(String method,
String encoding, boolean indenting)
public OutputFormat(Document doc)
public OutputFormat(Document doc,
String encoding, boolean indenting)
public String getMethod()
public void setMethod(String method)
public String getVersion()
public void setVersion(String version)
public int getIndent()
public boolean getIndenting()
public void setIndent(int indent)
public void setIndenting(boolean on)
public String getEncoding()
public void setEncoding(String encoding)
public String getMediaType()
public void setMediaType(String mediaType)
public void setDoctype(String publicID, String systemID)
public String getDoctypePublic()
public String getDoctypeSystem()
public boolean getOmitXMLDeclaration()
public void setOmitXMLDeclaration(boolean omit)
public boolean getStandalone()
public void setStandalone(boolean standalone)
public String[] getCDataElements()
public boolean isCDataElement(String tagName)
public void setCDataElements(String[] cdataElements)
public String[] getNonEscapingElements()
public boolean isNonEscapingElement(String tagName)
public void setNonEscapingElements(String[] nonEscapingElements)
public String getLineSeparator()
public void setLineSeparator(String lineSeparator)
public boolean getPreserveSpace()
public void setPreserveSpace(boolean preserve)
public int getLineWidth()
public void setLineWidth(int lineWidth)
public char getLastPrintable()
public static String whichMethod(Document doc)
public static String whichDoctypePublic(Document doc)
public static String whichDoctypeSystem(Document doc)
public static String whichMediaType(String method)
}
Latin-1 encoding
Indentation
Word wrapping
Document type declaration
try {
// Now that the document is created we need to *serialize* it
OutputFormat format = new OutputFormat(fibonacci, "8859_1", true);
format.setLineSeparator("\r\n");
format.setLineWidth(72);
format.setDoctype(null, "fibonacci.dtd");
XMLSerializer serializer = new XMLSerializer(System.out, format);
serializer.serialize(root);
}
catch (IOException e) {
System.err.println(e);
}
Question: Why won't this let us add an xml-stylesheet
directive?
<?xml version="1.0" encoding="8859_1"?>
<!DOCTYPE Fibonacci_Numbers SYSTEM "fibonacci.dtd">
<Fibonacci_Numbers>
<fibonacci index="0">0</fibonacci>
<fibonacci index="1">1</fibonacci>
<fibonacci index="2">1</fibonacci>
<fibonacci index="3">2</fibonacci>
<fibonacci index="4">3</fibonacci>
<fibonacci index="5">5</fibonacci>
<fibonacci index="6">8</fibonacci>
<fibonacci index="7">13</fibonacci>
<fibonacci index="8">21</fibonacci>
<fibonacci index="9">34</fibonacci>
<fibonacci index="10">55</fibonacci>
<fibonacci index="11">89</fibonacci>
<fibonacci index="12">144</fibonacci>
<fibonacci index="13">233</fibonacci>
<fibonacci index="14">377</fibonacci>
<fibonacci index="15">610</fibonacci>
<fibonacci index="16">987</fibonacci>
<fibonacci index="17">1597</fibonacci>
<fibonacci index="18">2584</fibonacci>
<fibonacci index="19">4181</fibonacci>
<fibonacci index="20">6765</fibonacci>
<fibonacci index="21">10946</fibonacci>
<fibonacci index="22">17711</fibonacci>
<fibonacci index="23">28657</fibonacci>
<fibonacci index="24">46368</fibonacci>
<fibonacci index="25">75025</fibonacci>
</Fibonacci_Numbers>
import org.apache.xerces.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;
import org.apache.xerces.dom.*;
import org.apache.xml.serialize.*;
public class DOMPrettyPrinter {
public static void main(String[] args) {
DOMParser parser = new DOMParser();
for (int i = 0; i < args.length; i++) {
try {
// Read the entire document into memory
parser.parse(args[i]);
Document document = parser.getDocument();
OutputFormat format
= new OutputFormat(document, "UTF-8", true);
format.setLineSeparator("\r\n");
format.setIndenting(true);
format.setIndent(2);
format.setLineWidth(72);
format.setPreserveSpace(false);
XMLSerializer serializer
= new XMLSerializer(System.out, format);
serializer.serialize(document);
}
catch (SAXException e) {
System.err.println(e);
}
catch (IOException e) {
System.err.println(e);
}
}
} // end main
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- <!DOCTYPE foo SYSTEM "http://msdn.microsoft.com/xml/general/htmlentities.dtd"> -->
<weblogs>
<log>
<name>MozillaZine</name>
<url>http://www.mozillazine.org</url>
<changesUrl>http://www.mozillazine.org/contents.rdf</changesUrl>
<ownerName>Jason Kersey</ownerName>
<ownerEmail>kerz@en.com</ownerEmail>
<description>THE source for news on the Mozilla Organization.
DevChats, Reviews, Chats, Builds, Demos, Screenshots, and more.</description>
<imageUrl/>
<adImageUrl>http://static.userland.com/weblogMonitor/ads/kerz@en.com.gif</adImageUrl>
</log>
<log>
<name>SalonHerringWiredFool</name>
<url>http://www.salonherringwiredfool.com/</url>
<ownerName>Some Random Herring</ownerName>
<ownerEmail>salonfool@wiredherring.com</ownerEmail>
<description/>
</log>
<log>
<name>Scripting News</name>
<url>http://www.scripting.com/</url>
<ownerName>Dave Winer</ownerName>
<ownerEmail>dave@userland.com</ownerEmail>
<description>News and commentary from the cross-platform scripting community.</description>
<imageUrl>http://www.scripting.com/gifs/tinyScriptingNews.gif</imageUrl>
<adImageUrl>http://static.userland.com/weblogMonitor/ads/dave@userland.com.gif</adImageUrl>
</log>
<log>
<name>SlashDot.Org</name>
<url>http://www.slashdot.org/</url>
<ownerName>Simply a friend</ownerName>
<ownerEmail>afriendofweblogs@weblogs.com</ownerEmail>
<description>News for Nerds, Stuff that Matters.</description>
</log>
</weblogs>
Using the DOM to write documents automatically maintains well-formedness constraints
Validity is not automatically maintained.
This presentation: http://www.cafeconleche.org/slides/sd2005west/saxdom
Elliotte Rusty Harold
Addison Wesley, 2002
Chapters 9-13:
Chapter 9, The Document Object Model: http://www.cafeconleche.org/books/xmljava/chapters/ch09.html
Chapter 10, Creating New XML Documents with DOM: http://www.cafeconleche.org/books/xmljava/chapters/ch10.html
Chapter 11, The Document Object Model Core: http://www.cafeconleche.org/books/xmljava/chapters/ch11.html
Chapter 12, The DOM Traversal Module: http://www.cafeconleche.org/books/xmljava/chapters/ch12.html
Chapter 13, Output from DOM: http://www.cafeconleche.org/books/xmljava/chapters/ch13.html
XML in a Nutshell, third edition
Elliotte Rusty Harold and W. Scott Means
O'Reilly & Associates, 2004
ISBN 0-596-00764-7
DOM Level 2 Core Specification: http://www.w3.org/TR/DOM-Level-2-Core/
DOM Level 2 Traversal and Range Specification: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/