Alternative to IndentingXMLStreamWriter.java - java

Is their an alternative to IndentingXMLStreamWriter.java i've always had some sort of issue at some point in which i am unable to compile though it goes away after a while. So I was wondering if their was an alternate way to indent manually parsed XML files
though the error message is slightly differnt when it is compiled as part of a netbeans module... the paths are alterered with ~ for anyone thats wondering =p
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\MasterDeckXMLImporterExporter.java:5: package com.sun.xml.internal.txw2.output does not exist
import com.sun.xml.internal.txw2.output.IndentingXMLStreamWriter;
Note: Attempting to workaround 6512707
warning: No processor claimed any of these annotations: [javax.xml.bind.annotation.XmlValue, javax.xml.bind.annotation.XmlSeeAlso, javax.xml.bind.annotation.XmlAccessorType, javax.xml.bind.annotation.XmlRootElement, javax.xml.bind.annotation.XmlAttribute]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\MasterDeckXMLImporterExporter.java:5: package com.sun.xml.internal.txw2.output does not exist
import com.sun.xml.internal.txw2.output.IndentingXMLStreamWriter;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\MasterDeckXMLImporterExporter.java:68: cannot find symbol
symbol : class IndentingXMLStreamWriter
location: class com.spectre.util.MasterDeckXMLImporterExporter
xsw = new IndentingXMLStreamWriter(xsw);
2 errors
3 warnings
C:\Program Files\jmonkeyplatform\harness\suite.xml:182: The following error occurred while executing this line:
C:\Program Files\jmonkeyplatform\harness\common.xml:206: Compile failed; see the compiler error output for details.
Just to be a bit clear on things this would be how i would use stax
import com.sun.xml.internal.txw2.output.IndentingXMLStreamWriter;
XMLStreamWriter xsw = XMLOutputFactory.newInstance().createXMLStreamWriter(new FileOutputStream(new File("Blah")));
xsw = new IndentingXMLStreamWriter(xsw);
xsw.writeStartDocument();
xsw.writeStartElement("map");
for (Map.Entry<String, Date> entry : map.entrySet()) {
xsw.writeEmptyElement("entry1");
xsw.writeAttribute("Name", entry.getKey());
xsw.writeAttribute("date", sdf.format(entry.getValue()));
}
xsw.writeEndElement();
xsw.writeEndDocument();
xsw.close();

You could use Saxon. In the s9api interface, you can do something like
Processor p = new Processor();
Serializer s = p.newSerializer(System.out);
s.setOutputProperty(Property.INDENT, "yes");
XMLStreamWriter w = s.getXMLStreamWriter();
and then you have an indenting serializer that implements the XMLStreamWriter interface, with many more formatting options available if you want to play with them.

If you parse your XML to an instance of org.w3c.Document (e.g. using DocumentBuilderFactory), you could try the following.
Using Apache Xerces:
Document doc = ...;
OutputFormat format = new OutputFormat(doc);
format.setIndenting(true);
format.setIndent(2);
XMLSerializer serializer = new XMLSerializer(out, format);
serializer.serialize(doc);
Or using the standard TransformerFactory:
Document doc = ...;
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
t.transform(new DOMSource(doc), new StreamResult(out));

Related

Saxon 9 HE, Java - Static errors, XTSE0210, XTSE0165, XPST0017

When invoking an XSL transform using Saxon from my application I receive the following error
Static error at xsl:import on line 34 column 45
XTSE0210: A stylesheet cannot import itself
Static error at xsl:import on line 42 column 39
XTSE0165: Reported 1 error in imported stylesheet module
Static error in {leg:IsCurrentWelsh(/)} in expression in xsl:when/#test on line 101 column 43
XPST0017: Cannot find a 1-argument function named
.
.
.
net.sf.saxon.trans.XPathException: Errors were reported during stylesheet compilation
at net.sf.saxon.style.StylesheetModule.loadStylesheet(StylesheetModule.java:260) ~[Saxon-HE-9.8.0-15.jar:na]
at net.sf.saxon.style.Compilation.compileSingletonPackage(Compilation.java:106) ~[Saxon-HE-9.8.0-15.jar:na]
at net.sf.saxon.s9api.XsltCompiler.compile(XsltCompiler.java:739) ~[Saxon-HE-9.8.0-15.jar:na]
at net.sf.saxon.jaxp.SaxonTransformerFactory.newTemplates(SaxonTransformerFactory.java:155) ~[Saxon
However when invoking Saxon from the command line it works successfully.
java -jar saxon9he.jar xml.xml {path-to-my-xslt}
The code invoking the XSLT in my Java application is as follows...
public static String transform(Document inputDoc, String xslDoc, Map<String, Object> params, String xslContextPath) throws XmlException {
try {
System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
TransformerFactory factory = TransformerFactory.newInstance();
factory.setURIResolver(new ClasspathResourceURIResolver(xslContextPath));
factory.setAttribute(FeatureKeys.GENERATE_BYTE_CODE, false);
Templates template = factory.newTemplates(new StreamSource(new StringReader(xslDoc)));
Transformer xformer = template.newTransformer();
if (params != null) {
for (Map.Entry<String, Object> entry : params.entrySet()) {
xformer.setParameter(entry.getKey(), entry.getValue());
}
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
DOMSource domSource = new DOMSource(inputDoc);
xformer.transform(domSource, new StreamResult(outputStream));
return outputStream.toString("UTF-8");
} catch (TransformerConfigurationException e) {
throw new XmlException(e);
} catch (TransformerException e) {
SourceLocator locator = e.getLocator();
if (locator != null) {
Map<String, Object> message = new HashMap<String, Object>();
message.put("col", locator.getColumnNumber());
message.put("line", locator.getLineNumber());
message.put("publicId", locator.getPublicId());
message.put("systemId", locator.getSystemId());
throw new XmlException(message.toString(), e);
}
throw new XmlException(e);
} catch (Exception e) {
throw new XmlException(e);
}
}
no other params are being passed in for either case.
This is using Saxon 9.8.0-15he. In most cases the above code is working fine and we have been using it for a long time without issue, it is when I am invoking a particular XSLT, which has a series of imports, too large to reproduce here.
Any idea what may need tweaking in the code to help it work?
Strangely running the same code through Saxon 9.4he, works.
When you do
Templates template = factory.newTemplates(new StreamSource(new StringReader(xslDoc)));
you're not providing a system ID (base URI) for the stylesheet. By contrast, when you run from the command line, Saxon can work out a base URI from the supplied filename. I don't know exactly why it's failing, because you haven't provided enough information, but I suspect this is the basic cause of the difference. Because Saxon has incomplete information about base URIs of stylesheet modules, it probably thinks two modules are the same when they aren't.
You can supply a system ID as the second argument of new StreamSource().

How to append XML (String) to XmlEventWriter (StAX) which already created the start of document

In order to create big XML files, we decided to make use of the StAX API. The basic structure is build by using the low-level api's: createStartDocument(), createStartElement(). This works as expected.
However, in some cases we like to append existing XML data which resides in a String (retrieved from database). The following snippet illustrates this:
import java.lang.*;
import java.io.*;
import javax.xml.stream.*;
public class Example {
public static void main(String... args) throws XMLStreamException {
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
XMLEventFactory eventFactory = XMLEventFactory.newInstance();
StringWriter writer = new StringWriter();
XMLEventWriter eventWriter = outputFactory.createXMLEventWriter(writer);
eventWriter.add(eventFactory.createStartDocument("UTF-8", "1.0"));
eventWriter.add(eventFactory.createStartElement("ns0", "http://example.org", "root"));
eventWriter.add(eventFactory.createNamespace("ns0", "http://example.org"));
//In here, we want to append a piece of XML which is stored in a string variable.
String xml = "<fragments><fragment><data>This is pure text.</data></fragment></fragments>";
eventWriter.add(inputFactory.createXMLEventReader(new StringReader(xml)));
eventWriter.add(eventFactory.createEndDocument());
System.out.println(writer.toString());
}
}
With the above code, depending on the implementation, we are not getting the expected result:
Woodstox: The following exception is thrown:'Can not output XML declaration, after output has already been done'. It seems that the XMLEventReader starts off with a startDocument event, but since a startDocument event was already triggered programatically, it throws the error.
JDK: It appends <?xml version="1.0" ... <fragments><fragment>... -> Which leads to invalid XML.
I have also tried to append the XML by using:
eventFactory.createCharacters(xml);
The problem here is that even though the XML is appended, the < and > are transformed into &lt and &gt. Therefore, this results in invalid XML.
Am I missing an API that allows me to simply append a String as XML?
You can first consume any StartDocument if necessary:
String xml = "<fragments><fragment><data>This is pure text.</data></fragment></fragments>";
XMLEventReader xer = inputFactory.createXMLEventReader(new StringReader(xml));
if (xer.peek().isStartDocument())
{
xer.nextEvent();
}
eventWriter.add(xer);

Creating XML from XPATH

I am a new bee with xml and XSL, work with legacy platforms...
I am looking for a solution to create XML from XPATH. Happen to see this post How to Generate an XML File from a set of XPath Expressions?
which helped me a lot.
Similar to the request discussed under "Comments" section, I am trying to pass whole XSLT as string, and receiving result as a sting back using Saxon. Receiving result as string, no issues. But when passing XSL as string, it complain about "document()" which is part of < xsl:variable name="vStylesheet" select="document('')" />. Error is "SXXP0003: Error reported by XML parser: Content is not allowed in prolog."
My basic requirement is I should be able to pass XSL (whole file or "vPop" portion) as a string and should receive result in another string without involving any files. That way I can improve the performance and make it generic so that anyone in our shop who does not know how to deal with XML can still generate one...
My java code looks like..
public static String simpleTransform(final String xsltStr) {
String strResult = "";
TransformerFactory tFactory = TransformerFactory.newInstance();
String tempXMLStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<OpnXMLTAG>Dummy</OpnXMLTAG>";
try {
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
StreamSource XSLSource = new StreamSource(new StringReader(xsltStr));
Transformer transformer = tFactory.newTransformer(XSLSource );
transformer.transform(new StreamSource(new StringReader(tempXMLStr)), result);
strResult = writer.toString();
} catch (Exception e) {
e.printStackTrace();
}
return strResult;
}
And XSLT string I am passing is the same from earlier post.
When you call document(''), it treats '' as a relative URI reference to be resolved against the base URI of the stylesheet. This won't work if the base URI of the stylesheet is unknown, which is the case when you supply it as a StreamSource wrapping a StringReader with no systemId.
In the case of Saxon, document('') actually needs to re-read the stylesheet. It doesn't keep the source file around at run-time just in case it's needed. So you'll need to (a) supply a URI as the systemId property on the StreamSource (any URI will do, it won't actually be read), and (b) supply a URIResolver to resolve the call on document('') and supply the original string.

Java program to convert a Text file to XML using XSL

I am trying to implement a small example where I want to convert content in a text file to XML file using XSL as transformer. I came across this example - XSL - create well formed xml from text file in SO and I was trying to implement the same but facing some issues.
I am using the same text file as input and the XSL file mentioned in answer of the SO post. This is the Java program I am trying to use:
public class Parser {
public static void main(String[] args) {
String path="src/";
String text = path+"input.txt";
String xslt = path+"input.xsl";
String output = path+"output.xml";
System.setProperty("javax.xml.transform.TransformerFactory",
"net.sf.saxon.TransformerFactoryImpl");
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer tr = tf.newTransformer(new StreamSource(xslt));
tr.transform(new StreamSource(text), new StreamResult(
new FileOutputStream(output)));
System.out.println("Output to " + output);
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
}
}
I am getting exception as:
Error on line 1 column 1 of input.txt:
SXXP0003: Error reported by XML parser: Content is not allowed in prolog.
net.sf.saxon.trans.XPathException: org.xml.sax.SAXParseException: Content is not allowed in prolog.
net.sf.saxon.trans.XPathException: org.xml.sax.SAXParseException: Content is not allowed in prolog.
at net.sf.saxon.event.Sender.sendSAXSource(Sender.java:418)
at net.sf.saxon.event.Sender.send(Sender.java:214)
at net.sf.saxon.event.Sender.send(Sender.java:50)
at net.sf.saxon.Controller.transform(Controller.java:1611)
at three.Parser.main(Parser.java:21)
Caused by: org.xml.sax.SAXParseException: Content is not allowed in prolog.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:195)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:174)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:388)
at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1427)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:1036)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:647)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:140)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
at net.sf.saxon.event.Sender.sendSAXSource(Sender.java:404)
... 4 more
It seems I cannot use the text file as input in my program. Can someone please help me in fixing the issue.
Update:
I have solved it using the Saxon S9 API (using Jar - saxon9he.jar) as suggested by Martin in his answer, here is the JAVA code that worked.
import java.io.File;
import javax.xml.transform.stream.StreamSource;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
import net.sf.saxon.s9api.XsltCompiler;
import net.sf.saxon.s9api.XsltExecutable;
import net.sf.saxon.s9api.XsltTransformer;
public class Parser {
public static void main(String[] args) throws SaxonApiException {
Processor proc = new Processor(false);
XsltCompiler comp = proc.newXsltCompiler();
XsltExecutable exp = comp.compile(new StreamSource(new File(
"src/input.xsl")));
Serializer out = new Serializer();
out.setOutputProperty(Serializer.Property.METHOD, "xml");
out.setOutputProperty(Serializer.Property.INDENT, "yes");
out.setOutputFile(new File("src/output.xml"));
XsltTransformer trans = exp.load();
trans.setInitialTemplate(new QName("main"));
trans.setDestination(out);
trans.transform();
System.out.println("Output written to text file");
}
}
The code to transform text to XML depends on XSLT version 2.0 and an XSLT 2.0 processor like Saxon 9. The JAXP API you are trying to use is solely useful with an XSLT 1.0 approach of having an XML input document as the primary source to the XSLT code. Thus if you want to use that API then you need to make sure you pass a dummy input XML to the transformer, while the URI of the plain text file should be passed in as a parameter. I would however suggest to use the Saxon S9 API to simply start the stylesheet with a named template main, also passing in the plain text URI as a parameter.
You can't feed plain text to an XSL transformer. It only accepts well-formed XML as input.
So the code in the linked question starts the transformer with no input and then inside of XSLT, it loads the text with
<xsl:variable name="csv" select="unparsed-text($pathToCSV, $encoding)" />

StAX XML formatting in Java

Is it possible using StAX (specifically woodstox) to format the output xml with newlines and tabs, i.e. in the form:
<element1>
<element2>
someData
</element2>
</element1>
instead of:
<element1><element2>someData</element2></element1>
If this is not possible in woodstox, is there any other lightweight libs that can do this?
There is com.sun.xml.txw2.output.IndentingXMLStreamWriter
XMLOutputFactory xmlof = XMLOutputFactory.newInstance();
XMLStreamWriter writer = new IndentingXMLStreamWriter(xmlof.createXMLStreamWriter(out));
Using the JDK Transformer:
public String transform(String xml) throws XMLStreamException, TransformerException
{
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
Writer out = new StringWriter();
t.transform(new StreamSource(new StringReader(xml)), new StreamResult(out));
return out.toString();
}
Via the JDK: transformer.setOutputProperty(OutputKeys.INDENT, "yes");.
If you're using the StAX cursor API, you can indent the output by wrapping the XMLStreamWriter in an indenting proxy. I tried this in my own project and it worked nicely.
Rather than relying on a com.sun...class that might go away (or get renamed com.oracle...class), I recommend downloading the StAX utility classes from java.net. This package contains a IndentingXMLStreamWriter class that works nicely. (Source and javadoc are included in the download.)
How about StaxMate:
http://www.cowtowncoder.com/blog/archives/2006/09/entry_21.html
Works well with Woodstox, fast, low-memory usage (no in-memory tree built), and indents like so:
SMOutputFactory sf = new SMOutputFactory(XMLOutputFactory.newInstance());
SMOutputDocument doc = sf.createOutputDocument(new FileOutputStream("output.xml"));
doc.setIndentation("\n ", 1, 2); // for unix linefeed, 2 spaces per level
// write doc like:
SMOutputElement root = doc.addElement("element1");
root.addElement("element2").addCharacters("someData");
doc.closeRoot(); // important, flushes, closes output
If you're using the iterating method (XMLEventReader), can't you just attach a new line '\n' character to the relevant XMLEvents when writing to your XML file?
Not sure about stax, but there was a recent discussion about pretty printing xml here
pretty print xml from java
this was my attempt at a solution
How to pretty print XML from Java?
using the org.dom4j.io.OutputFormat.createPrettyPrint() method
if you are using XMLEventWriter, then an easier way to do that is:
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
XMLEventWriter writer = outputFactory.createXMLEventWriter(w);
XMLEventFactory eventFactory = XMLEventFactory.newInstance();
Characters newLine = eventFactory.createCharacters("\n");
writer.add(startRoot);
writer.add(newLine);
With Spring Batch this requires a subclass since this JIRA BATCH-1867
public class IndentingStaxEventItemWriter<T> extends StaxEventItemWriter<T> {
#Setter
#Getter
private boolean indenting = true;
#Override
protected XMLEventWriter createXmlEventWriter( XMLOutputFactory outputFactory, Writer writer) throws XMLStreamException {
if ( isIndenting() ) {
return new IndentingXMLEventWriter( super.createXmlEventWriter( outputFactory, writer ) );
}
else {
return super.createXmlEventWriter( outputFactory, writer );
}
}
}
But this requires an additionnal dependency because Spring Batch does not include the code to indent the StAX output:
<dependency>
<groupId>net.java.dev.stax-utils</groupId>
<artifactId>stax-utils</artifactId>
<version>20070216</version>
</dependency>

Categories

Resources