9.3. Creating a Document with StAX streaming

To create a new document using the StAX API, we must first create an XMLStreamWriter that provides methods to produce XML opening and closing tags, attributes and character content. As this approach only keeps a small part of the document in memory, there is no validation or even well-formedness guarantee on the program. The methods must be called in the appropriate order in order to reflect the XML tree structure.

The most frequently XMLStreamWriter (xmlsw) methods are:

xmlsw.writeStartDocument();
initialises an empty document to which elements can be added
xmlsw.writeStartElement(String s)
creates a new element named s
xmlsw.writeAttribute(String name, String value)
adds the attribute name with the corresponding value to the last element produced by a call to writeStartElement. It is possible to add attributes as long as no call to writeElementStart, writeCharacters or writeEndElement has been done.
xmlsw.writeEndElement
close the last started element
xmlsw.writeCharacters(String s)
creates a new text node with content s as content of the last started element.

Like we did in Section 9.1, to simplify parsing, we create a customized StreamTokenizer (line 18‑2) which returns a single token for all characters between separators used in the compact form (i.e. opening and closing brackets, at-sign and newline). The separators are also returned as a single token. The implementation of this tokenizer is given in Example 9.1.

The main class for the StAX transformation (Example 9.5) is very simple: it creates an XMLStreamWriter instance that will write the output file (line 17‑1) and an instance of a CompactTokenizer to read the input file. After skipping everything before the first opening bracket, we initialize the output with a start document (line 26‑3) and call expand method (line 29‑4) to deal with the content of an element. As the output of an XMLStreamWriter is not indented, we decided here to define the ignorableSpacing method (line 73‑13) that outputs whitespace nodes. This is usually not needed but it is convenient to be able to see the results. In this particular case, creating a properly indented is a bit tricky because we must output a new line only when the last thing written was the end of an element or an attribute.

The expand method (line 43‑7) first outputs the attributes on the current element (line 46‑8) and then writes either a new element and calls itself recursively to process its contents (line 57‑10) or it outputs the character content of the element (line 63‑11).

Example 9.5. [StAXExpand.java]: XML document creation using the StAX approach

  1 import  java.io.BufferedReader;
    import  java.io.FileReader;
    import  java.io.IOException;
    
  5 import javax.xml.stream.XMLOutputFactory;
    import javax.xml.stream.XMLStreamException;
    import javax.xml.stream.XMLStreamWriter;
    
    import java.util.Arrays;
 10 
    public class StAXExpand {   
        static XMLStreamWriter xmlsw = null;
        public static void main(String[] argv) {
            try {
 15             xmlsw = XMLOutputFactory.newInstance()
                              .createXMLStreamWriter(System.out);
                CompactTokenizer tok = new CompactTokenizer(             (1)
                              new FileReader(argv[0]));                  (2)
                
 20             String rootName = "dummyRoot";
                // ignore everything preceding the word before the first "["
                while(!tok.nextToken().equals("[")){
                    rootName=tok.getToken();
                }
 25             // start creating new document
                xmlsw.writeStartDocument();                              (3)
                ignorableSpacing(0);
                xmlsw.writeStartElement(rootName);
                expand(tok,3);                                           (4)
 30             ignorableSpacing(0);
                xmlsw.writeEndDocument();                                (5)
                
                xmlsw.flush();
                xmlsw.close();
 35         } catch (XMLStreamException e){                              (6)
                System.out.println(e.getMessage());
            } catch (IOException ex) {
                System.out.println("IOException"+ex);
                ex.printStackTrace();
 40         }
        }
    
        public static void expand(CompactTokenizer tok, int indent)      (7)
            throws IOException,XMLStreamException {
 45         tok.skip("["); 
            while(tok.getToken().equals("@")) {// add attributes         (8)
                String attName = tok.nextToken();
                tok.nextToken();
                xmlsw.writeAttribute(attName,tok.skip("["));
 50             tok.nextToken();
                tok.skip("]");
            }
            boolean lastWasElement=true; // for controlling the output of newlines 
            while(!tok.getToken().equals("]")){ // process content       (9)
 55             String s = tok.getToken().trim();
                tok.nextToken();
                if(tok.getToken().equals("[")){                          (10)
                    if(lastWasElement)ignorableSpacing(indent);
                    xmlsw.writeStartElement(s);
 60                 expand(tok,indent+3);
                    lastWasElement=true;
                } else {
                    xmlsw.writeCharacters(s);                            (11)
                    lastWasElement=false;
 65             }
            }
            tok.skip("]");
            if(lastWasElement)ignorableSpacing(indent-3);
            xmlsw.writeEndElement();                                     (12)
 70    }
        
        private static char[] blanks = "\n".toCharArray();
        private static void ignorableSpacing(int nb)                     (13)
            throws XMLStreamException {
 75         if(nb>blanks.length){// extend the length of space array 
                blanks = new char[nb+1];
                blanks[0]='\n';
                Arrays.fill(blanks,1,blanks.length,' ');
            }
 80         xmlsw.writeCharacters(blanks, 0, nb+1);
        }
    
    }
    

1

Creates the XMLStreamWriter for outputting the XML text.

2

Creates a specialized tokenizer for the compact form.

3

Initializes the XML document.

4

Calls the expanding process on the root element.

5

Terminates the XML writing proces by ending the document and closing the file.

6

Deals with exceptions that can be raised during writing the XML code and the file.

7

Recursive method to output an element properly indented.

8

Adds attributes name and value on the last element outputted.

9

Deals with the content of the element.

10

Recursively calls the expansion on the content of the element.

11

Outputs the character content.

12

Outputs the end of the current element.

13

Creates a character node containing a new line and an appropriate number of spaces.