I racked my head against the wall over and over again for several hours, unable to determine why I was getting a prolog error, when I knew dang well my XML was well formed.
I’m using JAXP, which is detecting and using xalan as my transformation implementation. I’m not sure who’s fault it is, but when I create a new StreamSource for my transformation, and I ask it to load the xml-stylesheet from the processing instruction, it simply doesn’t work. I keep getting one of two errors, depending on the format of the java String.
The first error I was getting was “javax.xml.transform.TransformerException: Content is not allowed in prolog
“. The other error I was getting, once I put my xml declaration at the beginning of the string, was “javax.xml.transform.TransformerException: The markup in the document following the root element must be well-formed
“. Of course, none of these are meaningful in any way. Neither one tells me that I’m not allowed to ask xalan to resolve my xml-stylesheet automatically, while using XML from a Java String object.
I started out with this…
xmlSource = new StreamSource( new ByteArrayInputStream(((String) xml).getBytes())) xsltSource = transFact.getAssociatedStylesheet(xmlSource, null, null, null); ... trans.transform(xmlSource, result); |
Of course, that completely fails with the miserable errors described. But alas, after several ours of messing around, I decided to look into the source of “org.apache.xalan.xslt.Process”. It happens to use DOMSource, rather than StreamSource. So, I tried that out, and it worked just fine.
And, just to help you out with a nice utility function for doing xalan transformations…
/** * Runs a xalan transformation of the xml, with the specified xsl. * * @param xml a String xml document, or a java.io.File object * pointing to the file * @param xsl a String filename, or a java.io.File object pointing to * the file. A null value indicates you want to resolve * the XML's "xml-stylesheet" processing instruction as * the XSL to use. If it does not exist, it may fail. * @param parameters a map of parameters to pass to the XSL * * @return the String of the transformed xml * * @throws TransformerException if a transformation error occurs */ public static String xalanTransformation(final Object xml, final Object xsl, final Map parameters) throws TransformerException, ParserConfigurationException, IOException, SAXException { // create an instance of TransformerFactory final ByteArrayOutputStream byteArray; final Result result; final TransformerFactory transFact; final Transformer trans; final Set keys; final Iterator keyIt; final ExceptionErrorListener errorListener; errorListener = new ExceptionErrorListener(); byteArray = new ByteArrayOutputStream(BUFFER_CAPACITY); result = new StreamResult(byteArray); transFact = TransformerFactory.newInstance(); transFact.setErrorListener(errorListener); final Source xmlSource; if (xml instanceof String) { final Document doc = stringToDocument((String) xml); logger.debug(documentToString(doc)); xmlSource = new DOMSource(doc); } else if (xml instanceof File) { xmlSource = new StreamSource((File) xml); } else { throw new IllegalArgumentException( "Only java.lang.String and java.io.File xml are supported " + "for the xml parameter"); } final Source xsltSource; if (xsl == null) { // grab the XSL defined by the XML's xml-stylesheet instruction xsltSource = transFact.getAssociatedStylesheet(xmlSource, null, null, null); } else if (xsl instanceof String) { final InputStream xsltResource = Util.class.getResourceAsStream( (String) xsl); if (xsltResource == null) { throw new IllegalArgumentException( xsl + " is an invalid XSL file"); } xsltSource = new StreamSource(xsltResource); } else if (xsl instanceof File) { xsltSource = new StreamSource((File) xsl); } else { throw new IllegalArgumentException( "Only java.lang.String xsl filenames, or java.io.File " + "are supported for the xsl parameter"); } trans = transFact.newTransformer(xsltSource); trans.setErrorListener(errorListener); keys = parameters.keySet(); keyIt = keys.iterator(); while (keyIt.hasNext()) { final String key; key = (String) keyIt.next(); // assume string key trans.setParameter(key, parameters.get(key)); } trans.transform(xmlSource, result); return byteArray.toString(); } /** * Example for retrieving the APAS institutions list * * @param xml the string representation of the XML * * @return the Document object created from the XML string representation * * @throws IOException if an I/O error occurs * @throws SAXException if an XML parsing exception occurs. */ public static Document stringToDocument(final String xml) throws SAXException, IOException { return loadXMLFrom(new ByteArrayInputStream(xml.getBytes())); } public static Document loadXMLFrom(final InputStream is) throws SAXException, IOException { final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder = null; try { builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException ignored) { // throw something here if you like. } assert builder != null; final Document doc = builder.parse(is); is.close(); return doc; } |
Example usage is below. Take note how I pass in null for the XSL, as the XSL can be loaded from the “xml-stylesheet” processing instruction in the XML document. The utility method above requests resolution of the stylesheet from the XML, if the “xsl” parameter is null.
System.out.println(Util.xalanTransformation(xml, null, new HashMap()));