Tuesday, October 20, 2009

A hint for indenting with XMLStreamWriter

Default implementation of javax.xml.stream.XMLStreamWriter does not support such output features as indentation and multi-line output. Correspondingly the resulting text is always a long one-line string which many of us would want to format in a pretty style for better reading. There is a solution bundled with Java 6 although it is sort of internal Sun facility which may have gone one beautiful day.:-) The class is named com.sun.xml.internal.txw2.output.IndentingXMLStreamWriter, it is public, and it resides in rt.jar. Using it is a matter of couple of lines in your code:
        final XMLStreamWriter defaultWriter =
            XMLOutputFactory.newInstance
().createXMLStreamWriter(writer);
       
final IndentingXMLStreamWriter sw = new IndentingXMLStreamWriter(defaultWriter);
        sw.setIndentStep
("    ");

As shown above it allows you to vary the indent character sequence giving certain flexibility by that.
The only problem I can think of might be due to the end-of-line character internally hard-coded by '\n'. I would prefer having system-dependent or, better, a user-provided line terminator instead.
P.S. Just discovered that this class is only included into JRE not into JDK. That means one sure way to solve the original problem is to duplicate the class source in your application. There is nothing special in its logic as it routinely decorates an instance of XMLStreamWriter with the required functionality. The source code for JDK 6 can be downloaded from this page.

3 comments:

  1. Good solution, but for some reasons we can't use "com.sun.*" classes in code.

    I found another way with Transformer

    out = new BufferedOutputStream(new FileOutputStream(exportFile));
    bout = new ByteArrayOutputStream();

    XMLStreamWriter writer = createWriter(bout);

    writeData(writer, profiles.get(0));

    writer.flush();
    writer.close();

    // Transform XML to get new with indentation.
    TransformerFactory factory = TransformerFactory.newInstance();

    Transformer transformer = factory.newTransformer();
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");

    transformer.transform(new StreamSource(new ByteArrayInputStream(bout.toByteArray())),
    new StreamResult(out));

    ReplyDelete
  2. To amagdenko:
    Also possible, though the effect on memory for extra storage needs to be considered thoroughly. In some applications this may increase GC work too much. Our system was processing lots of XML messages so we tried to minimize memory consumption where we could.

    Actually I had initially started with writing my implementation of a XMLStreamWriter, but then discovered the Sun's solution and decided to use it instead. Laziness, I guess...:-D

    ReplyDelete
  3. @amagendko:
    Thanks for the useful comment. I am a beginner of Java programming, using Eclipse. I didnt manage to install com.sun.* classes, so I have used your method. It worked perfectly for me.

    ReplyDelete