bea.com | products | dev2dev | support | askBEA

Download Docs | Site Map | Glossary
Search

Programming WebLogic XML

Using the WebLogic XML Streaming API

The following sections describe how to use the WebLogic XML Streaming API to parse and generate XML documents:

- Overview of the WebLogic XML Streaming API

- Javadocs for the WebLogic XML Streaming API

- Parsing an XML Document: Typical Steps

- Generating a New XML Document: Typical Steps

Overview of the WebLogic XML Streaming API

The WebLogic XML Streaming API provides an easy and intuitive way to parse and generate XML documents. It is similar to the SAX API, but enables a procedural, stream-based handling of XML documents rather than requiring you to write SAX event handlers, which can get complicated when you work with complex XML documents. In other words, the streaming API gives you more control over parsing than the SAX API.

When a program parses an XML document using SAX, the program must create event listeners that listen to parsing events as they occur; the program must react to events rather than ask for a specific event. By contrast, when you use the streaming API, you can methodically step through an XML document, ask for certain types of events (such as the start of an element), iterate over the attributes of an element, skip ahead in the document, stop processing at any time, get sub-elements of a particular element, and filter out elements as desired. Because you are asking for events rather than reacting to them, using the streaming API is often referred to as pull parsing.

You can parse many types of XML documents with the streaming API, such as XML files on the operating system, DOM trees, and sets of SAX events. You convert these XML documents into a stream of events, or an XMLInputStream, and then step through the stream, pulling events such as the start of an element, the end of the document, and so on, off the stack as needed.

The WebLogic Streaming API uses the WebLogic FastParser as its default parser.

For a complete example of parsing an XML document using the streaming API, see the WL_HOME\samples\server\src\examples\xml\orderParser directory, where WL_HOME refers to the top-level WebLogic Platform directory.

The following table describes the main interfaces and classes of the WebLogic Streaming API.

Table 4-1 Interfaces and Classes of the XML Streaming API

Interface or Class | Description
--- | ---
XMLInputStreamFactory | Factory used to create XMLInputStream objects for parsing XML documents.
XMLInputStream | Interface used to contain the input stream of events.
BufferedXMLInputStream | Extension of the XMLInputStream interface to allow marking and resetting of the stream.
XMLOutputStreamFactory | Factory used to create XMLOutputStream objects for generating XML documents.
XMLOutputStream | Interface used write events.
ElementFactory | Utility to create instances of the interfaces used in this API.
XMLEvent | Base interface for all types of events in an XML document, such as the start of an element, the end of an element, and so on.
StartElement | Most important of the XMLEvent sub-interfaces. Used to get information about a start element in an XML document.
AttributeIterator | Object used to iterate over the set of attributes of an element.
Attribute | Object that describes a particular attribute of an element.

Javadocs for the WebLogic XML Streaming API

The following Javadocs provide reference material for the WebLogic XML Streaming API features described in this chapter as well as additional features not explicitly documented:

- weblogic.xml.stream

- weblogic.xml.stream.util

Parsing an XML Document: Typical Steps

The following procedure describes the typical steps for using the WebLogic XML Streaming API to parse and manipulate an XML document.

The first two steps are required. The next steps you take depend on how you want to process the XML file.

1. Import the weblogic.xml.stream.* classes.

2. Get an XML stream of events from an XML file, a DOM tree, or a set of SAX events. You can also filter the XML stream to get only certain types of events, names of specific elements, and so on. See "Getting an XML Input Stream" on page 4-7.

3. Iterate over the stream, returning generic XMLEvent types. See "Iterating Over the Stream" on page 4-10.

4. For each generic XMLEvent type, determine the specific event type. Event types include the start of an XML document, the end of an element, an entity reference, and so on. See "Determining the Specific XMLEvent Type" on page 4-11.

5. Get the attributes of an element. See "Getting the Attributes of an Element" on page 4-15.

6. Position the stream by skipping over event, skipping to a particular event, and so on. See "Positioning the Stream" on page 4-16.

7. Get the children of an element. See "Getting a Substream" on page 4-17.

8. Close the stream. See "Closing the Input Stream" on page 4-19.

Example of Parsing an XML Document

The following program shows an example of using the XML Streaming API to parse an XML document.

The program takes a single parameter, an XML file, that it converts into an XML input stream. It then iterates over the stream, determining the type of each event, such as the start of an XML element, the end of the XML document, and so on. The program prints out information for three types of events: start elements, end elements, and the character data that forms the body of an element. The program does nothing when it encounters the other types of events, such as comments or start of the XML document.

Note: The code in bold font is described in detail in the sections following the example.

>     package examples.xml.stream;

>     import weblogic.xml.stream.Attribute;
>     import weblogic.xml.stream.AttributeIterator;
>     import weblogic.xml.stream.ChangePrefixMapping;
>     import weblogic.xml.stream.CharacterData;
>     import weblogic.xml.stream.Comment;
>     import weblogic.xml.stream.XMLEvent;
>     import weblogic.xml.stream.EndDocument;
>     import weblogic.xml.stream.EndElement;
>     import weblogic.xml.stream.EntityReference;
>     import weblogic.xml.stream.ProcessingInstruction;
>     import weblogic.xml.stream.Space;
>     import weblogic.xml.stream.StartDocument;
>     import weblogic.xml.stream.StartPrefixMapping;
>     import weblogic.xml.stream.StartElement;
>     import weblogic.xml.stream.EndPrefixMapping;
>     import weblogic.xml.stream.XMLInputStream;
>     import weblogic.xml.stream.XMLInputStreamFactory;
>     import weblogic.xml.stream.XMLName;
>     import weblogic.xml.stream.XMLStreamException;

>     import java.io.FileInputStream;
>     import java.io.FileNotFoundException;

>     public class ComplexParse {

>       /**
>        * Helper method to get a handle on a stream.
>        * Takes in a name and returns a stream.  This
>        * method usese the InputStreamFactory to create an
>        * instance of an XMLInputStream
>        * @param name The file to parse
>        * @return XMLInputStream the stream to parse
>        */
>       public XMLInputStream getStream(String name)
>         throws XMLStreamException, FileNotFoundException
>       {
>         XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance();
>         XMLInputStream stream = factory.newInputStream(new FileInputStream(name));
>         return stream;
>       }

>       /**
>        *  Determines the type of event, such as the start
>        *  of an element, end of a document, and so on.  If the
>        *  event is of type START_ELEMENT, END_ELEMENT, or
>        *  CHARACTER_DATA, the method prints out appropriate info;
>        *  otherwise, it does nothing.
>        *  @param event The XML event that has been parsed
>        */
>       public void parse(XMLEvent event)
>         throws XMLStreamException
>       {
>         switch(event.getType()) {
>         case XMLEvent.START_ELEMENT:
>           StartElement startElement = (StartElement) event;
>           System.out.print("<" + startElement.getName().getQualifiedName() );
>           AttributeIterator attributes = startElement.getAttributesAndNamespaces();
>           while(attributes.hasNext()){
>             Attribute attribute = attributes.next();
>             System.out.print(" " + attribute.getName().getQualifiedName() +
>                               "='" + attribute.getValue() + "'");
>           }
>           System.out.print(">");
>           break;
>         case XMLEvent.END_ELEMENT:
>           System.out.print("</" + event.getName().getQualifiedName() +">");
>           break;
>         case XMLEvent.SPACE:
>         case XMLEvent.CHARACTER_DATA:
>           CharacterData characterData = (CharacterData) event;
>           System.out.print(characterData.getContent());
>           break;
>         case XMLEvent.COMMENT:
>           // Print comment
>           break;
>         case XMLEvent.PROCESSING_INSTRUCTION:
>           // Print ProcessingInstruction
>           break;
>         case XMLEvent.START_DOCUMENT:
>           // Print StartDocument
>           break;
>         case XMLEvent.END_DOCUMENT:
>           // Print EndDocument
>           break;
>         case XMLEvent.START_PREFIX_MAPPING:
>           // Print StartPrefixMapping
>           break;
>         case XMLEvent.END_PREFIX_MAPPING:
>           // Print EndPrefixMapping
>           break;
>         case XMLEvent.CHANGE_PREFIX_MAPPING:
>           // Print ChangePrefixMapping
>           break;
>         case XMLEvent.ENTITY_REFERENCE:
>           // Print EntityReference
>           break;
>         case XMLEvent.NULL_ELEMENT:
>           throw new XMLStreamException("Attempt to write a null event.");
>         default:
>           throw new XMLStreamException("Attempt to write unknown event
>                                         ["+event.getType()+"]");
>         }
>       }
>       /**
>        * Helper method to iterate over a stream
>        * @param name The file to parse
>        */
>       public void parse(XMLInputStream stream)
>         throws XMLStreamException
>       {
>         while(stream.hasNext()) {
>           XMLEvent event = stream.next();
>           parse(event);
>         }
>         stream.close();
>       }

>       /** Main method.   Takes a single argument: an XML file
>        *  that will be converted into an XML input stream.
>        */
>       public static void main(String args[])
>         throws Exception
>       {
>         ComplexParse complexParse= new ComplexParse();
>         complexParse.parse(complexParse.getStream(args[0]));
>       }
>     }

Getting an XML Input Stream

You can use the XML Streaming API to convert a variety of objects, such as XML files, DOM trees, or SAX events, into a stream of events.

The following example shows how to create a stream of events from an XML file:

>     XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance();
>     XMLInputStream stream = factory.newInputStream(new FileInputStream(name));

First you create a new instance of the XMLInputStreamFactory, then use the factory to create a new XMLInputStream from the XML file referred to in the name variable.

The following example shows how to create a stream from a DOM tree:

>     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
>     dbf.setValidating(false);
>     dbf.setNamespaceAware(true);
>     DocumentBuilder db = dbf.newDocumentBuilder();
>     Document doc = db.parse(new java.io.File(file));
>     XMLInputStream stream = XMLInputStreamFactory.newInstance().newInputStream(doc);

Getting a Buffered XML Input Stream

After you finish iterating over an XMLInputStream object, you cannot access the stream again. If, however, you need to process the stream again, such as send it to another application or iterate over it again in some other way, use a BufferedXMLInputStream object rather than a plain XMlInputStream object.

Use the newBufferedInputStream() method of the XMLInputStreamFactory class to create a buffered XMlL input stream, as shown in the following example:

>     XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance();
>     BufferedXMLInputStream bufstream =
>         factory.newBufferedInputStream(factory.newInputStream(new
>         FileInputStream(name)));

You can use the mark() and reset() methods of the BufferedXMLInputStream object to mark a particular spot in the stream, continue processing the stream, then reset the stream back to the marked spot. See Marking and Resetting a Buffered XML Input Stream for more information.

Filtering the XML Stream

Filtering an XML stream refers to creating a stream that contains only specified types of events. For example, you can create a stream that contains only start elements, end elements, and the character data that make up the body of an XML element. Another example is filtering an XML stream so that only elements with a specified name appear in the stream.

To filter an XML stream, you specify a filter class as the second parameter to the XMLInputStreamFactory.newInputStream() method. You specify the events that you want in the XML stream as parameters to the filter class. The following example shows how to use the TypeFilter class to specify that you want only start and end XML elements and character data in the resulting XML stream:

>     import weblogic.xml.stream.util.TypeFilter;

>     XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance();
>     XMLInputStream stream = factory.newInputStream(new FileInputStream(name),
>                             new TypeFilter(XMLEvent.START_ELEMENT |
>                                            XMLEvent.END_ELEMENT |
>                                            XMLEvent.CHARACTER_DATA));

The following table describes the filters provided by the WebLogic XML Streaming API. They are part of the weblogic.xml.stream.util package.

Table 4-2

Name of Filter | Description | Sample Usage
--- | --- | ---
TypeFilter | Filter an XML stream based on specified event types, such as XMLEvent.START_ELEMENT, XMLEvent.END_ELEMENT, and so on. See "Determining the Specific XMLEvent Type" on page 4-11 for a full list of event types. TypeFilter takes an integer bitmask as input; you OR the values to create this bitmask, as shown in the sample. | new TypeFilter (XMLEvent.START_ELEMENT | XMLEvent.END_ELEMENT | XMLEvent.CHARACTER_DATA)
NameFilter | Filter an XML stream based on the name of an element in the XML document. | new NameFilter ("Book")
NameSpaceFilter | Filter an XML stream based on the specified namespace URI. | new NameSpaceFilter ("http://namespace.org")
NamespaceTypeFilter | Filter an XML stream based on specified event types and namespace URI. This filter combines the functionality of TypeFilter and NameSpaceFilter. | new NamespaceFilter ("http://namespace.org", XMLEvent.START_ELEMENT) The example returns a stream where all start elements have the specified namespace.

Creating a Custom Filter

You can also create your own filter if the ones included in the API do not meet your needs.

1. Create a class that implements the ElementFilter interface and contains a method called accept(XMLEvent). This method tells the XMLInputStreamFactory.newInputStream() method whether to add a particular event to the stream or not, as shown in the following example:

   >     package my.filters;

   >     import weblogic.xml.stream.XMLName;
   >     import weblogic.xml.stream.ElementFilter;
   >     import weblogic.xml.stream.events.NullEvent;

   >     public class SuperDooperFilter implements ElementFilter {

   >       protected String name;

   >       public SuperDooperFilter(String name)
   >       {
   >         this.name = name;
   >       }

   >       public boolean accept(XMLEvent e) {
   >         if (name.equals(e.getName().getLocalName()))
   >           return true;
   >         return false;
   >       }
   >     }

2. In your XML application, be sure to import the new filter class:

   >     import my.filters.SuperDooperFilter

3. Specify the filter as the second parameter to the newInputStream() method, passing to the filter class the types of events you want to appear in the XML stream in whatever format required by your filter class:

>     XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance();
>     XMLInputStream stream = factory.newInputStream(new FileInputStream(name),
>                             new SuperDooperFilter(param));

Iterating Over the Stream

Once you have a stream of events, the next step is to methodically step through it using the XMLInputStream.next() and XMLInputStream.hasNext() methods, as shown in the following example:

>         while(stream.hasNext()) {
>           XMLEvent event = stream.next();
>           System.out.print(event);
>         }

Determining the Specific XMLEvent Type

The XMLInputStream.next() method returns an object of type XMLEvent. XMLEvent has subinterfaces that further classify what this event might be, such as the start of the XML document, the end of an element, an entity reference, and so on. The XMLEvent interface also contains corresponding fields, or constants, as well as a set of methods that you can use to identify the actual event. The following diagram shows the hierarchy of the XMLEvent interface and its subinterfaces:

The following table lists the subclasses and fields of the XMLEvent class that you can use to identify a particular event while parsing the XML stream.

Table 4-3 Subclasses and Fields of the XMLEvent Class

XMLEvent Subclass | Field of the XMLEvent Class used to Identify Subclass | Method used to Identify Subclass | Description of the Subclass Event
--- | --- | --- | ---
ChangePrefixMapping | CHANGE_PREFIX_MAPPING | isChangePrefixMapping | Signals that a prefix mapping has changed from an old namespace to a new namespace.
CharacterData | CHARACTER_DATA | isCharacterData | Signals that the returned XMLEvent object contains the character data from the body of the element.
Comment | COMMENT | isComment | Signals that the returned XMLEvent object contains an XML comment.
EndDocument | END_DOCUMENT | isEndDocument | Signals the end of the XML document.
EndElement | END_ELEMENT | isEndElement | Signals the end of an element in the XML document.
EndPrefixMapping | END_PREFIX_MAPPING | isEndPrefixMapping | Signals that a prefix mapping has gone out of scope.
EntityReference | ENTITY_REFERENCE | isEntityReference | Signals that the returned XMLEvent object contains an entity reference.
ProcessingInstruction | PROCESSING_INSTRUCTION | isProcessingInstruction | Signals that the returned XMLEvent object contains a processing instruction.
Space | SPACE | isSpace | Signals that the returned XMLEvent object contains whitespace.
StartDocument | START_DOCUMENT | isStartDocument | Signals the start of an XML document.
StartElement | START_ELEMENT | isStartElement | Signals the start of a element in the XML document.
StartPrefixMapping | START_PREFIX_MAPPING | isStartPrefixMapping | Signals that a prefix mapping has started its scope.

The following example shows how to use the Java case statement to determine the particular type of event that was returned by the XMLInputStream.next() method. For simplicity, the example simply prints that an event has been found; later sections show further processing of the event.

>         switch(event.getType()) {
>         case XMLEvent.START_ELEMENT:
>           // Start of an element
>           System.out.println ("Start Element\n");
>           break;

>         case XMLEvent.END_ELEMENT:
>           // End of an element
>           System.out.println ("End Element\n");
>           break;

>         case XMLEvent.PROCESSING_INSTRUCTION:
>           // Processing Instruction
>           System.out.println ("Processing instruction\n");
>           break;

>         case XMLEvent.SPACE:
>           // Whitespace
>           System.out.println ("White space\n");
>           break;

>         case XMLEvent.CHARACTER_DATA:
>           // Character data
>           System.out.println ("Character data\n");
>           break;

>         case XMLEvent.COMMENT:
>           // Comment
>           System.out.println ("Comment\n");
>           break;

>         case XMLEvent.START_DOCUMENT:
>           // Start of the XML document
>           System.out.println ("Start Document\n");
>           break;

>         case XMLEvent.END_DOCUMENT:
>           // End of the XML Document
>           System.out.println ("End Document\n");
>           break;

>         case XMLEvent.START_PREFIX_MAPPING:
>           // The start of a prefix mapping scope
>           System.out.println ("Start prefix mapping\n");
>           break;

>         case XMLEvent.END_PREFIX_MAPPING:
>           // The end of a prefix mapping scope
>           System.out.println ("End prefix mapping\n");
>           break;

>         case XMLEvent.CHANGE_PREFIX_MAPPING:
>           // Prefix mapping has changed namespaces
>           System.out.println ("Change prefix mapping\n");
>           break;

>         case XMLEvent.ENTITY_REFERENCE:
>           // An entity reference
>           System.out.println ("Entity reference\n");
>           break;
>         default:

>           throw new XMLStreamException("Attempt to parse unknown event
>                                         [" + event.getType() + "]");
>         }

Getting the Attributes of an Element

To get the attributes of an element in an XML document, you must first cast the XMLEvent object that was returned by the XMLInputStream.next() method to a StartElement object.

Because you do not know how many attributes an element might have, you must first create an AttributeIterator object to contain the entire list of attributes, and then iterate over the list until there are no more attributes. The following example describes how to do this as part of the START_ELEMENT case of the switch statement shown in "Iterating Over the Stream" on page 4-10:

>     case XMLEvent.START_ELEMENT:

>         StartElement startElement = (StartElement) event;
>         System.out.print("<" + startElement.getName().getQualifiedName() );
>         AttributeIterator attributes = startElement.getAttributesAndNamespaces();
>         while(attributes.hasNext()){
>           Attribute attribute = attributes.next();
>           System.out.print(" " + attribute.getName().getQualifiedName() +
>                             "='" + attribute.getValue() + "'");
>         }
>         System.out.print(">");
>         break;

The example first creates a StartElement object by casting the returned XMLEvent to StartElement. It then creates an AttributeIterator object using the method StartElement.getAttributesAndNamespaces(), and iterates over the attributes using the AttributeIterator.hasNext() method. For each Attribute, it uses the Attributes.getName().getQualifiedName() and Attribute.getValue() methods to return the name and value of the attribute.

You can also use the getNamespace() and getAttributes() methods to return just the namespaces or attributes on their own.

Positioning the Stream

The following table describes the methods of the XMLInputStream interface that you can use to skip ahead to specific locations in the stream.

Table 4-4 Methods Used to Position the Input Stream

Method of XMLInputStream | Description
--- | ---
skip() | Positions the input stream to the next stream event. Note: The next event might not necessarily be an actual element in the XML file; for example, it could be a comment or white space.
skip(int) | Positions the input stream to the next event of this type. Examples of event types are XMLEvent.START_ELEMENT and XMLEvent.END_DOCUMENT. Refer to Table 4-3 for the full list of event types.
skip(XMLName) | Positions the input stream to the next event of this name.
skip(XMLName, int) | Positions the input stream to the next event of this name and type.
skipElement() | Skips to the next element (does not skip to the sub-elements of the current element).
peek() | Checks the next event without actually reading it from the stream.

The following example shows how you can modify the basic code for iterating over an input stream to skip over the character data in the body of an XML element:

>         while(stream.hasNext()) {
>           XMLEvent peek = stream.peek();
>           if (peek.getType() == XMLEvent.CHARACTER_DATA ) {
>             stream.skip();
>             continue;
>           }
>           XMLEvent event = stream.next();
>           parse(event);
>         }

The example shows how to use the XMLInputStream.peek() method to determine the next event on the stream. If the type of event is XMLEvent.CHARACTER_DATA, then skip the event and go to the next one.

Getting a Substream

Use the XMLInputStream.getSubStream() method to get a copy of the next element, including all its subelements. The getSubstream() method returns an XMLInputStream object. Your position in the parent stream (or the stream from which you called getSubStream()) does not move. In the parent stream, if you want to skip the element you just got with getSubStream(), use the skipElement() method.

The getSubStream() method keeps a count of the START_ELEMENT and END_ELEMENT events it encounters, and as soon as the number is equal (or in other words, as soon as it finds the complete next element) it stops and returns the resulting substream as an XMLInputStream object.

For example, assume that you are using the XML Streaming API to parse the following XML document, but you are only interested in the substream delineated by the <content> and </content> tags:

>     <book>
>       <title>The History of the World</title>
>       <author>Juliet Shackell</author>
>       <publisher>CrazyDays Publishing</publisher>
>       <content>
>           <chapter title='Just a Speck of Dust'>
>             <synopsis>The world as a speck of dust</synopsis>
>             <para>Once the world was just a speck of dust...</para>
>           </chapter>
>           <chapter title='Life Appears'>
>             <synopsis>Move over dust, here comes life.</synopsis>
>             <para>Happily, the dust got a companion: life...</para>
>           </chapter>
>        </content>
>     </book>

The following code fragment shows how you can skip to the <content> start element tag, get the substream, and parse it using a separate ComplexParse object:

>          if (stream.skip( ElementFactory.createXMLName("content")))
>            {
>               ComplexParse complexParse = new ComplexParse();
>               complexParse.parse(stream.getSubStream());
>            }

When you call this method on the previous XML document, you get the following output:

>     <content>
>           <chapter title='Just a Speck of Dust'>
>             <synopsis>The world as a speck of dust</synopsis>
>             <para>Once the world was just a speck of dust...</para>
>           </chapter>
>           <chapter title='Life Appears'>
>             <synopsis>Move over dust, here comes life.</synopsis>
>             <para>Happily, the dust got a companion: life...</para>
>           </chapter>
>     </content>

Marking and Resetting a Buffered XML Input Stream

If you are using a BufferedXMLInputStream object, you can use the mark() and reset() methods to mark the stream at a particular spot, process the stream, and then subsequently reset the stream back to the marked spot. These methods are useful if you want to further manipulate the stream after initially iterating over it.

Note: If you read a buffered stream without marking it, you cannot access what you've just read. In other words, just because the stream is buffered, it does not automatically mean you can reread it. You must mark it first.

The following example shows a typical use of the BufferedXMLInputStream object:

>         XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance();
>         BufferedXMLInputStream bufstream =
>         factory.newBufferedInputStream(factory.newInputStream(new
>                     FileInputStream(name)));

>         // mark the start of the stream
>         bufstream.mark();

>         // process it locally
>         bufferedParse.parse(bufstream);

>         // reset the stream to the mark
>         bufstream.reset();

>         // send stream off to another application
>         ComplexParse complexParse =  new ComplexParse();
>         complexParse.parse(bufstream);

Closing the Input Stream

It is good programming practice to explicitly close the XML input stream when you are finished with it. To close an input stream, use the XMLInputStream.close() method, as shown in the following example:

>     // close the input stream
>     input.close();

Generating a New XML Document: Typical Steps

The following procedure describes the typical steps for using the WebLogic XML Streaming API to generate a new XML document.

The first two steps are required. The next steps you take depend on how you want to generate the XML file.

1. Import the weblogic.xml.stream.* classes.

2. Create an XML output stream to which to write the XML document. See "Creating an XML Output Stream" on page 4-22.

3. Add events to the XML output stream. See "Adding Elements to the Output Stream" on page 4-23.

4. Add attributes to the XML output stream. See "Adding Attributes to an Element on the Output Stream" on page 4-24.

5. Add an input stream to the output stream. See "Adding an Input Stream to an Output Stream" on page 4-25.

6. Print the output stream. See "Printing an Output Stream" on page 4-26.

7. Close the output stream. See "Closing the Output Stream" on page 4-26.

Example of Generating an XML Document

The following program shows an example of using the XML Streaming API to generate an XML document.

The program first creates an output stream based on a PrintWriter object, then adds elements to the output stream to create a simple XML purchase order, described in the comments of the program. The program also shows how to add an input stream based on a separate XML file to the output stream.

Note: The topics following the example describe it in more detail.

>     package examples.xml.stream;

>     import weblogic.xml.stream.XMLInputStream;
>     import weblogic.xml.stream.XMLOutputStream;
>     import weblogic.xml.stream.XMLInputStreamFactory;
>     import weblogic.xml.stream.XMLName;
>     import weblogic.xml.stream.XMLEvent;
>     import weblogic.xml.stream.StartElement;
>     import weblogic.xml.stream.EndElement;
>     import weblogic.xml.stream.Attribute;
>     import weblogic.xml.stream.ElementFactory;
>     import weblogic.xml.stream.XMLStreamException;
>     import weblogic.xml.stream.XMLOutputStreamFactory;

>     import java.io.FileInputStream;
>     import java.io.FileNotFoundException;
>     import java.io.PrintWriter;

>     /**
>      * Program that prints out a very simple purchase order that looks
>      * like the following:
>      *
>      * <purchase_order>
>      *  <name>Juliet Shackell</name>
>      *  <item id="1234" quantity="2">Fabulous Chair</item>
>      *  <!-- this is a comment-->
>      *  <another_file>
>      *    This comes from another file called "another_file.xml"
>      *  </another_file>
>      * </purchase_order>
>      *
>      * In the preceding XML file, the <another_file> element is actually another
>      * XML file that is passed as an argument to the program, converted into an
>      * XMLInputStream, then added to the output stream.
>      */
>     public class PrintPurchaseOrder {

>       /**
>        * Helper method to get a handle on a stream.
>        * Takes in a name and returns a stream.  This
>        * method uses the InputStreamFactory to create an
>        * instance of an XMLInputStream
>        * @param name The file to parse
>        * @return XMLInputStream the stream to parse
>        */
>       public XMLInputStream getInputStream(String name)
>         throws XMLStreamException, FileNotFoundException
>       {
>         XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance();
>         XMLInputStream stream = factory.newInputStream(new FileInputStream(name));
>         return stream;
>       }
>       public static void main(String args[])
>         throws Exception
>       {
>         PrintPurchaseOrder printer = new PrintPurchaseOrder();
>         //
>         // Create an output stream.
>         //
>         XMLOutputStreamFactory factory = XMLOutputStreamFactory.newInstance();
>         XMLOutputStream output = factory.newOutputStream(new
>                                             PrintWriter(System.out,true));

>        // add the <purchase_order> root element
>        output.add(ElementFactory.createStartElement("purchase_order"));
>        output.add(ElementFactory.createCharacterData("\n"));

>        // add the <name> element
>        output.add(ElementFactory.createStartElement("name"));
>        output.add(ElementFactory.createCharacterData("Juliet Shackell"));
>        output.add(ElementFactory.createEndElement("name"));
>        output.add(ElementFactory.createCharacterData("\n"));

>        // add the <item> element along with the id and quantity attributes
>        output.add(ElementFactory.createStartElement("item"));
>        output.add(ElementFactory.createAttribute("id","1234"));
>        output.add(ElementFactory.createAttribute("quantity","2"));
>        output.add(ElementFactory.createCharacterData("Fabulous Chair"));
>        output.add(ElementFactory.createEndElement("item"));
>        output.add(ElementFactory.createCharacterData("\n"));

>         // add a comment
>        output.add("<!-- this is a comment-->");
>        output.add(ElementFactory.createCharacterData("\n"));

>        // create an input stream from each XML file argument then add it to the output
>        for (int i=0; i < args.length; i++)
>        //
>        // Get an input stream and add it to the output stream
>        //
>        output.add(printer.getInputStream(args[i]));

>         // Finally, end the root "purchase_order" element
>        output.add(ElementFactory.createEndElement("purchase_order"));
>        output.add(ElementFactory.createCharacterData("\n"));

>         //
>         // Print the results to the screen
>         //
>         output.flush();

>        // Close the output streams
>        output.close();
>       }
>     }

The preceding program produces the following output:

>     <purchase_order>
>       <name>Juliet Shackell</name>
>       <item id="1234" quantity="2">Fabulous Chair</item>
>       <!-- this is a comment-->
>       <another_file>
>          This is from another file.
>       </another_file>
>     </purchase_order>

Creating an XML Output Stream

One of the first steps in generating an XML document using the Weblogic XML Streaming API is to create an output stream which holds the document as it is being built. Creating an XML output stream is similar to creating an input stream: you first create an instance of the XMLOutputStreamFactory and then create an output stream with the XMLOutputStreamFactory.newOutputStream() method, as shown in the following example:

>     	    XMLOutputStreamFactory factory = XMLOutputStreamFactory.newInstance();
>         XMLOutputStream output = factory.newOutputStream(new
>                                   PrintWriter(System.out,true));

The following example shows how to create an XMLOutputStream based on a DOM tree:

>         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
>         dbf.setValidating(false);
>         dbf.setNamespaceAware(true);
>         Document doc = dbf.newDocumentBuilder().newDocument();
>         XMLOutputStream out =
>              XMLOutputStreamFactory.newInstance().newOutputStream(doc);

You can use the XMLOutputStreamFactory.newOutputStream() method to create an output stream based on the following four Java objects, depending on what the final form of the XML document will be (such as a file on the operating system, a DOM tree, and so on):

- java.io.OutputStream

- java.io.Writer

- org.xml.sax.ContentHandler

- org.w3c.dom.Document

Adding Elements to the Output Stream

Use the XMLOutputStream.add(XMLEvent) method to add elements to the output stream. Use the ElementFactory to create the particular element.

The ElementFactory interface includes methods to create each type of element; the general format is ElementFactory.create XXX() where XXX refers to the particular element, such as createStartElement(), createCharacterData(), and so on. You can create most elements by passing the name as a String or as an XMLName.

Warning: The XMLOutputStream does not validate your XML.

Note: Each time you create a start element, you must explicitly also create an end element at some point. The same rule applies to creating a start document.

For example, assume you want to create the following snippet of XML:

>     <name>Georgina Shackell Green</name>

The Java code to add this element to an output stream is as follows:

>        output.add(ElementFactory.createStartElement("name"));
>        output.add(ElementFactory.createCharacterData("Georgina Shackell Green"));
>        output.add(ElementFactory.createEndElement("name"));
>        output.add(ElementFactory.createCharacterData("\n"));

The final createCharacterData() method adds a newline character to the output stream. This is optional, but useful if you want to create human-readable XML.

Adding Attributes to an Element on the Output Stream

Use the XMLOutputStream.add(Attribute) to add attributes to an element you have just created. Use the ElementFactory.createAttribute() method to create a particular attribute.

For example, assume you want to create the following snippet of XML:

>     <item id="1234" quantity="2">Fabulous Chair</item>

The Java code to add this element to an output stream is as follows:

>        output.add(ElementFactory.createStartElement("item"));
>        output.add(ElementFactory.createAttribute("id","1234"));
>        output.add(ElementFactory.createAttribute("quantity","2"));
>        output.add(ElementFactory.createCharacterData("Fabulous Chair"));
>        output.add(ElementFactory.createEndElement("item"));
>        output.add(ElementFactory.createCharacterData("\n"));

Note: Be sure you add attributes to an element after you create the start element but before you create the corresponding end element. Otherwise, although your code will compile successfully, you will get a runtime error when you try to run the program. For example, the following code returns an error because the attributes are added to the <item> element after the element has been explicitly ended:

>        output.add(ElementFactory.createStartElement("item"));
>        output.add(ElementFactory.createEndElement("item"));
>        output.add(ElementFactory.createAttribute("id","1234"));
>        output.add(ElementFactory.createAttribute("quantity","2"));
>        output.add(ElementFactory.createCharacterData("Fabulous Chair"));
>        output.add(ElementFactory.createCharacterData("\n"));

Adding an Input Stream to an Output Stream

When creating an XML output stream, you might want to add an existing XML document, such as an XML file or a DOM tree, to the output stream. To do this, you must first convert the XML document to an XML input stream, then use XMLOutputStream.add(XMLInputStream) method to add the input stream to the output stream.

The following example first shows a method called getInputStream() that creates an XML input stream from an XML file and then how to use the method to add the created input stream to an output stream:

>       /**
>        * Helper method to get a handle on a stream.
>        * Takes in a name and returns a stream.  This
>        * method uses the InputStreamFactory to create an
>        * instance of an XMLInputStream
>        * @param name The file to parse
>        * @return XMLInputStream the stream to parse
>        */

>       public XMLInputStream getInputStream(String name)
>         throws XMLStreamException, FileNotFoundException
>       {
>         XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance();
>         XMLInputStream stream = factory.newInputStream(new FileInputStream(name));
>         return stream;
>       }

>     ....

>        // create an input stream from each XML file argument then add it to the output

>        for (int i=0; i < args.length; i++)
>        //
>        // Get an input stream and add it to the output stream
>        //
>        output.add(printer.getInputStream(args[i]));

Printing an Output Stream

Use the XMLOutputStream.flush() method to print out the XML output stream to whatever object you created it from. For example, if you created an XML output stream from a PrintWriter object, then the flush() method prints the stream to the standard output.

Note: If you are writing to an XMLOutputStream based on a DOM tree, you must execute the flush() method before you can manipulate the DOM.

The following example shows how to print an output stream:

>         //
>         // Print the results to the screen
>         //
>         output.flush();

Closing the Output Stream

It is good programming practice to explicitly close the XML output stream when you are finished with it. To close an output stream, use the XMLOutputStream.close() method, as shown in the following example:

>     // close the output stream
>     output.close();