The second part of "introduction to StaxMate" needed before showing how
to actually use it, in the context of an actual web application, after
introducing the reader (input) side, is obviously introducing the writer
(output) side.
In some ways the writer side is simpler than the reader side: there is
only one general abstraction, that of "output entities", as defined by
SMOutputtable abstract base class. These output entities (sub-classes of
SMOutputtable) fall into 2 main categories: output containers that can
contain other entities (elements, fragments, and the document entity
belong to this class), and leaf entities (text, comments, processing
instructions, entity references). Although you conceptually create both
types, you will only directly manipulate the former: StaxMate will
construct entities of latter type if and as they are needed: most of the
time they can be directly output, and there is no need to even
instantiate these entities.
In addition to the output entities, namespaces are handled as
first-class objects (but very simple ones, from application
point-of-view): this is done both for design (cleaner to use, cleaner to
implement) and performance reasons (StaxMate can handle namespace
bindings very efficiently by using canonical namespace objects).
About the only advanced things beyond outputting things is the
possibility to temporarily "freeze" (buffer) output; and this is only
needed if out-of-order addition of output entities is needed. More on
this later on.
Finally, StaxMate output functionality also adds one often requested
feature that is missing from core Stax API: ability to intend
("pretty-print") XML output using simple heuristics. It can be enabled
on per-container basis, and is inherited as expected: thus, you can
control indentation as much or as little as you want: but most of the
time you will just want to enable it, and forget about it.
Having said all of that, here are typical steps one takes when
outputting XML content using StaxMate.
1. Getting started
Just like with StaxMate input cursors, the first thing is to instantiate
the underlying XMLStreamWriter needed, i.e. something like:
XMLOutputFactory f = XMLOutputFactory.newInstance();
XMLStreamWriter sw = f.createXMLStreamWriter(new
FileOutputStream("mydoc.xml", "UTF-8"));
2. Create the root-level container, configure
Somewhat similar to needing to create the root-level cursor, you need to
create a root-level output container. Here you have two choices: an
output document and output fragment. Former is used if you want to
output a whole xml document, latter if you need to be able to output
part of xml output (for example if you are outputting surrounding
document structure using XMLStreamWriter outside of StaxMate). You can
also enabled and configure indentation at this point:
SMOutputDocument doc = SMOutputFactory.createOutputDocument(sw, "1.0",
"UTF-8", true);
// Defines linefeed to use, spaces for indentation (from 1, step by 1)
doc.setIndentation("\n ", 1, 1);
3. Start outputting things
And then we are ready to start composing the output document:
SMOutputElement root = doc.addElement("root");
// But the leaves will be (suggests prefix "ns", defines URI)
SMNamespace ns = root.getNamespace("http://myns", "ns");
root.addElement(ns, "leaf"); // empty one
root.addElement(ns, "leaf").addCharacters("leaf text");
SMOutputElement leaf3 = root.addElement(ns, "leaf3");
leaf3.addAttribute("id", "leaf3"); // no-namespace attribute
leaf3.addComment("ad space for sale!");
// ... and so on
root.addComment("end of content");
/* This is important: root-level element MUST be closed,
* otherwise StaxMate can not determine when output is done
*/
doc.closeRoot();
4. The Limitation
Just like with the reader side, the main limitation is that since output
is done in streaming way, output will have to be done in the document
order: parent before children, and attributes after the element itself,
but before children.
But also just like with the reader side, there is a way to relax this
requirement a little, by using a feature that trades in added memory
usage with some added comfort. This is called "buffering", and is
explained next...
5. Advanced Feature(s)
As mentioned above, about the only advanced feature on the output side
is the ability to "freeze" output. This is done by using "buffered"
output entities, and the reason to use them is to allow out-of-order
output of xml content. Or, rather, allow temporary buffering of content
during assembling of all the output entities, so that the underlying
stream writer can be fed all the output entities in the strict document
order. StaxMate can handle all this smoothly, given just two things:
-
Create special entities that are to be buffered (and whose contents
will also be buffered, as long as the containing entity itself is)
-
Tell StaxMate when buffering is no longer needed ("release") for these
items -- you only need to indicate the buffered entity is to be
released, and all of its 'normal' contents will also be released.
The first step is done using one of two methods that all containers
(documents, fragments, elements) have:
createBufferedFragment()
createBufferedElement()
which results in creating of either a fragment, or element, in which
other entities can be added using normal output container add methods.
These resulting entities can then be added (possibly later, after doing
other non-buffered output), either as buffered, calling:
container.addBuffered(bufferedEntity);
and releasing (when all content to be added out-of-order has been added):
bufferedEntity.release();
or, if both steps can be combined, calling:
container.addAndReleaseBuffered(bufferedEntity);
This latter method obviously assumes that you have added other things
within the buffered entity -- if not, you could have just created a
normal element or fragment and added it.
6. Next Steps
Ok: after this crash course into basics of using StaxMate, we are about
ready to start writing the UUID Generator Web Application!