Last night, I was looking at generating SAMS XML from Java objects. It made me realise two things:
- Java sucks badly at creating XML (by default).
- I should really be looking at XML Binding.
I’m interested in point 1. I’ve done it before, trying to write XML using DOM (as there doesn’t appear to be a builtin SAX writer). This is what I ended up with.
public abstract class AbstractXmlBuilder {
protected void addXlinkHref(Element e, URI href) {
e.setAttributeNS(Constants.XLINK_NS, "x:href", href.toString());
}
// See http://www.cafeconleche.org/books/xmljava/chapters/ch09s09.html
// (JAXP Serialisation) for details on why all this palaver is necessary.
protected String domToString(Document doc) {
try {
TransformerFactory factory = TransformerFactory.newInstance();
Transformer trans = factory.newTransformer();
trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
DOMSource source = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult streamResult = new StreamResult(writer);
trans.transform(source, streamResult);
return writer.toString();
} catch (TransformerConfigurationException e) {
throw new RuntimeException(e);
} catch (TransformerFactoryConfigurationError e) {
throw new RuntimeException(e);
} catch (TransformerException e) {
throw new RuntimeException(e);
}
}
protected Document newDocument(String namespaceURI, String qualifiedName) {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder docBuilder = dbf.newDocumentBuilder();
DOMImplementation impl = docBuilder.getDOMImplementation();
return impl.createDocument(namespaceURI, qualifiedName, null);
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
}
}
protected Document newDocument() {
Document doc = newDocument(Constants.MY_NS, "doc");
Element root = doc.getDocumentElement();
root.setPrefix("my");
// Set up the usual namespaces.
root.setAttribute("xmlns:x", Constants.XLINK_NS);
return doc;
}
protected Element myElem(Document doc, String name) {
return doc.createElementNS(Constants.MY_NS, name);
}
}
This is intended to be subclassed to be useful, but even in the subclasses, it still relies on dozens of helper methods. e.g.
private Element getAccIdElem(SessionCreateRequest s, Document doc) {
Element accId = myElem(doc, "acc_id");
addXlinkHref(accId, s.getAccountId());
return accId;
}
This is incredibly verbose, but I don’t think we can do better with just the JDK. What are the other options? I’ve been looking at XOM. This appears to have a nice API for writing XML:
Element root = new Element("root");
root.appendChild("Hello World!");
Document doc = new Document(root);
System.out.println(doc.toXML());
This is much, much simpler. We’re still building a up a DOM-like structure, but it’s simpler to use.
But XOM is still a fairly large dependency. This doesn’t particularly matter for what I’m doing. But there are alternatives. Whilst looking for libraries, I also stumbled across xmlwriter. This is much lower-level:
Writer writer = new java.io.StringWriter();
XmlWriter xw = new SimpleXmlWriter(writer);
xw.writeXmlVersion();
xw.writeComment("Example of XmlWriter running");
xw.writeEntity("person");
xw.writeAttribute("name", "fred");
xw.writeAttribute("age", "12");
xw.writeEntity("phone");
xw.writeText("4254343");
xw.endEntity();
xw.writeComment("Examples of empty tags");
xw.writeEntity("friends");
xw.writeEmptyEntity("bob");
xw.writeEmptyEntity("jim");
xw.endEntity();
xw.writeEntityWithText("foo","This is an example.");
xw.endEntity();
xw.close();
System.err.println(writer.toString());
This is closer to genx, a C library that I like very much. It’s quite weird that “element” and “entity” appear to be confused, though. Plus there doesn’t appear to be any way to generate canonical XML.
For now, I think I’ll stick with XOM, but it’s good to know that there are alternatives to the heavyweight JDK options.