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.
One reply on “Writing XML in Java”
I’ve since found out about the recently released wax library , which seems like a useful player in this area. Judging by the examples, it seems to be pretty useful, although it annoys me that it can generate CDATA sections (why are they still around?) Seriously, a canonical XML output option would be nice.
As an example, this code:
Produces this output: