Setting namespaces and prefixes in a Java DOM document
java, serialization, transformer-model, xml
Solution
You haven't added the namespace declaration in the root node; you just declared the root node in the namespace, two entirely different things. When building a DOM, you need to reference the namespace on every relevant Node. In other words, when you add your attribute, you need to define its namespace (e.g., setAttributeNS).
Side note: Although XML namespaces look like URLs, they really aren't. There's no need to use the URL class here.
Problem
I'm trying to convert a ResultSet to an XML file. I've first used this example for the serialization. ``` import org.w3c.dom.bootstrap.DOMImplementationRegistry; import org.w3c.dom.Document; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSSerializer; ... DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); DOMImplementationLS impl = (DOMImplementationLS)registry.getDOMImplementation("LS"); ... LSSerializer writer = impl.createLSSerializer(); String str = writer.writeToString(document); ``` After I made this work, I tried to validate my XML file, there were a couple of warnings. One about not having a doctype. So I tried another way to implement this. I came across the Transformer class. This class lets me set the encoding, doctype, etc. The previous implementation supports automatic namespace fix-up. The following does not. ``` private static Document toDocument(ResultSet rs) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.newDocument(); URL namespaceURL = new URL("http://www.w3.org/2001/XMLSchema-instance"); String namespace = "xmlns:xsi="+namespaceURL.toString(); Element messages = doc.createElementNS(namespace, "messages"); doc.appendChild(messages); ResultSetMetaData rsmd = rs.getMetaData(); int colCount = rsmd.getColumnCount(); String attributeValue = "true"; String attribute = "xsi:nil"; rs.beforeFirst(); while(rs.next()) { amountOfRecords = 0; Element message = doc.createElement("message"); messages.appendChild(message); for(int i = 1; i <= colCount; i++) { Object value = rs.getObject(i); String columnName = rsmd.getColumnName(i); Element messageNode = doc.createElement(columnName); if(value != null) { messageNode.appendChild(doc.createTextNode(value.toString())); } else { messageNode.setAttribute(attribute, attributeValue); } message.appendChild(messageNode); } amountOfRecords++; } logger.info("Amount of records archived: " + amountOfRecords); TransformerFactory tff = TransformerFactory.newInstance(); Transformer tf = tff.newTransformer(); tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); tf.setOutputProperty(OutputKeys.INDENT, "yes"); BufferedWriter bf = createFile(); StreamResult sr = new StreamResult(bf); DOMSource source = new DOMSource(doc); tf.transform(source, sr); return doc; } ``` While I was testing the previous implementation I got an TransformationException: Namespace for prefix 'xsi' has not been declared. As you can see I've tried to add a namespace with the xsi prefix to the root element of my document. After testing this I still got the Exception. What is the correct way to set namespaces and their prefixes? Edit: Another problem I have with the first implementation is that the last element in the XML document doesn't have the last three closing tags.