I need to create an XML file dynamically. I tried using StAX with the XMLStreamWriter interface, but in this first code sample, the "cars" node tag is on the same line as the first XML node.
try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
transformerFactory.setAttribute("indent-number", 2);
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter inputStringWriter = new StringWriter();
XMLOutputFactory xMLOutputFactory = XMLOutputFactory.newInstance();
XMLStreamWriter xMLStreamWriter =
xMLOutputFactory.createXMLStreamWriter(inputStringWriter);
xMLStreamWriter.writeStartDocument();
xMLStreamWriter.writeStartElement("cars");
xMLStreamWriter.writeStartElement("supercars");
xMLStreamWriter.writeAttribute("company", "Ferrari");
xMLStreamWriter.writeStartElement("carname");
xMLStreamWriter.writeAttribute("type", "formula one");
xMLStreamWriter.writeCharacters("Ferrari 101");
xMLStreamWriter.writeEndElement();
xMLStreamWriter.writeStartElement("carname");
xMLStreamWriter.writeAttribute("type", "sports");
xMLStreamWriter.writeCharacters("Ferrari 202");
xMLStreamWriter.writeEndElement();
xMLStreamWriter.writeEndElement();
xMLStreamWriter.writeEndElement();
xMLStreamWriter.writeEndDocument();
xMLStreamWriter.flush();
xMLStreamWriter.close();
String xmlString = inputStringWriter.getBuffer().toString();
StringWriter outputStringWriter = new StringWriter();
StreamResult xmlOutput = new StreamResult(outputStringWriter);
Source xmlInput = new StreamSource(new StringReader(xmlString));
transformer.transform(xmlInput, xmlOutput);
FileOutputStream outputXml = new FileOutputStream(new File("./src/temp/myfile.xml"));
outputXml.write(xmlOutput.getWriter().toString().getBytes("UTF-8"));
inputStringWriter.close();
outputStringWriter.close();
System.out.println(xmlOutput.getWriter().toString());
} catch (XMLStreamException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
I tried using IndentingXMLStreamWriter to solve the above code problem, but to use it I need to import com.sun.xml.internal.txw2.output.IndentingXMLStreamWriter and SonarLint flags the import for me as a major quality defect.
try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setAttribute("indent-number", 4);
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter inputStringWriter = new StringWriter();
XMLOutputFactory xMLOutputFactory = XMLOutputFactory.newInstance();
XMLStreamWriter xMLStreamWriter =
new IndentingXMLStreamWriter(xMLOutputFactory.createXMLStreamWriter(inputStringWriter));
xMLStreamWriter.writeStartDocument();
xMLStreamWriter.writeStartElement("cars");
xMLStreamWriter.writeStartElement("supercars");
xMLStreamWriter.writeAttribute("company", "Ferrari");
xMLStreamWriter.writeStartElement("carname");
xMLStreamWriter.writeAttribute("type", "formula one");
xMLStreamWriter.writeCharacters("Ferrari 101");
xMLStreamWriter.writeEndElement();
xMLStreamWriter.writeStartElement("carname");
xMLStreamWriter.writeAttribute("type", "sports");
xMLStreamWriter.writeCharacters("Ferrari 202");
xMLStreamWriter.writeEndElement();
xMLStreamWriter.writeEndElement();
xMLStreamWriter.writeEndElement();
xMLStreamWriter.writeEndDocument();
xMLStreamWriter.flush();
xMLStreamWriter.close();
String xmlString = inputStringWriter.getBuffer().toString();
FileOutputStream outputXml = new FileOutputStream(new File("./src/temp/myfile.xml"));
outputXml.write(xmlString.getBytes("UTF-8"));
inputStringWriter.close();
System.out.println(xmlString);
} catch (XMLStreamException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
I am showing a simple XML structure as an example, but in reality the XML that I need to generate is much larger and with internal nodes that will be displayed depending on certain conditions.
Any recommendation?
CodePudding user response:
If you like the StreamWriter API but want more control over serialization, Saxon offers:
Processor proc = new Processor(false);
Serializer ser = proc.newSerializer(stringWriter);
ser.setOutputProperty(Serializer.Property.METHOD, "xml");
ser.setOutputProperty(Serializer.Property.INDENT, "yes");
XMLStreamWriter xMLStreamWriter = ser.getXMLStreamWriter();
...
I used the XMLStreamWriter
API for this purpose myself quite a lot, and eventually decided that it was clumsy and that it was possible to design something better. So Saxon now offers the Push API as an alternative. The main difference is that in the Push API, when you start a container node (document or element) it returns a handle that you use for constructing the children of that node. The result is that elements are self-closing, the API is far less stateful, and it's much harder to get yourself into the situation where you only discover at the end of the document that you've opened more elements than you've closed.
You example would look more like this:
Document doc = push.document();
Element cars = element("cars");
Element superCars = cars.element("supercars");
superCars.attribute("company", "Ferrari");
Element carName = superCars.element("carname");
carName.attribute("type", "formula one");
carName.text("Ferrari 101");
carName = superCars.element("carname");
carName.attribute("type", "sports");
carName.text("Ferrari 202");
doc.close();
It feels rather more DOM-like, but with the difference that nodes have to be created in the right order, because there is no in-memory tree being created.
CodePudding user response:
If you want to modify your code less, you may want to use this.
Add Transformer property like this (this remove first line declaring xml)
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
In the part of output, modify like this
StringBuffer xmlStringBuffer = new StringBuffer();
xmlStringBuffer.append("<?xml version=\"1.0\" ?>");
xmlStringBuffer.append(System.getProperty("line.separator"));
xmlStringBuffer.append(xmlOutput.getWriter().toString());
make file with this string buffer
outputXml.write(xmlStringBuffer.toString().getBytes("UTF-8"));