XMLStreamWriter: indentation
is there really no way to directly write formatted XML using javax.xml.stream.XMLStreamWriter (Java SE 6)??? This is really unbelievable, as other XML APIs such as JAXB and some DOM libraries are able to do this. Even the .NET XMLStreamWriter equivalent is able to this AFAIK (if I remember correctly the class is System.Xml.XmlTextWriter).
This means the only option I have is to reparse the XML to generate formatted output??
E.g.:
StringWriter sw = new StringWriter();
XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory();
XMLStreamWriter xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(sw);
writeXml(xmlStreamWriter);
xmlStreamWriter.flush();
xmlStreamWriter.close();
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
StringWriter formattedStringWriter = new StringWriter();
transformer.transform(new StreamSource(new StringReader(sw.toString())), new StreamResult(formattedStringWriter));
System.out.print开发者_如何学编程ln(formattedStringWriter);
The problem with this solution is the property "{http://xml.apache.org/xslt}indent-amount". I didn't find any documentation about it and it doesn't seem to be guaranteed to be portable.
So what other options do I have, if I want to do this with standard Java 6 classes? Create a JAXB or DOM object graph just for pretty printing??
This exact question has been answered some months ago and one of the answers is to use the IndentingXMLStreamWriter class:
XMLOutputFactory xmlof = XMLOutputFactory.newInstance();
XMLStreamWriter writer = new IndentingXMLStreamWriter(xmlof.createXMLStreamWriter(out));
It is a neat solution as far as the code goes, but careful: this is a com.sun.* class, there is no guarantee of its general availability...
You could add the necessary code to format your document in your writeXml method. Simply maintain a depth counter (to indicate the levels of nesting). Then before you writeStartElement and after you writeEndElement use the depth index to insert an indent.
for(int x=0; x<depth; x++) {
xmlStreamWriter.writeCharacters(" ");
}
I have no answer considering "{http://xml.apache.org/xslt}indent-amount"
property. But you can use transformers somewhat portably without the need to reparse all output. You can use something like the code below to create pretty-printing XMLStreamWriter
.
public static void main(String[] args) {
XMLStreamWriter xmlStreamWriter = createXMLStreamWriter(new OutputStreamWriter(System.out));
writeXml(xmlStreamWriter);
}
private static XMLStreamWriter createXMLStreamWriter(Writer textWriter) throws XMLStreamException {
SAXTransformerFactory xmlTransformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
TransformerHandler transformingSAXHandler;
try {
transformingSAXHandler = xmlTransformerFactory.newTransformerHandler();
} catch (TransformerConfigurationException e) {
throw new XMLStreamException(e);
}
Transformer xmlTransformer = transformingSAXHandler.getTransformer();
xmlTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformingSAXHandler.setResult(new StreamResult(textWriter));
XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
return xmlOutputFactory.createXMLStreamWriter(new SAXResult(transformingSAXHandler));
}
With Spring Batch this requires a subclass since this JIRA BATCH-1867
public class IndentingStaxEventItemWriter<T> extends StaxEventItemWriter<T> {
@Setter
@Getter
private boolean indenting = true;
@Override
protected XMLEventWriter createXmlEventWriter( XMLOutputFactory outputFactory, Writer writer) throws XMLStreamException {
if ( isIndenting() ) {
return new IndentingXMLEventWriter( super.createXmlEventWriter( outputFactory, writer ) );
}
else {
return super.createXmlEventWriter( outputFactory, writer );
}
}
}
But this requires an additionnal dependency because Spring Batch does not include the code to indent the StAX output:
<dependency>
<groupId>net.java.dev.stax-utils</groupId>
<artifactId>stax-utils</artifactId>
<version>20070216</version>
</dependency>
Just for the record, Saxon allows you to get a serializing XMLStreamWriter through the s9api interface:
Processor p = new Processor();
Serializer s = p.newSerializer();
s.setOutputProperty(Property.INDENT, "yes");
XMLStreamWriter w = s.getXMLStreamWriter();
and the Serializer exposes all the XSLT-defined output properties (including "indent") plus some Saxon-specific ones.
You are correct that the standard java.xml interfaces provide little to no control over serialization, even though the underlying implementation (Apache Xerces) does. I've had to solve this problem and the best I came up with was to include a copy of Xerces and use the org.apache.xml.serialize
classes.
精彩评论