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().
Related
I'm creating a single xml file uploader in my grails application. There is two types of files, Ap and ApWithVendor. I would like to auto detect the file type and convert the xml to the correct object using SAXParser.
What I've been doing is throwing an exception when the sax parser is unable to find a qName match within the the first Ap object using the endElement method. I then catch the exception and try the the ApWithVendor object.
My question is there a better way to do this without doing my condition checking with exceptions?
Code example
try {
System.out.println("ApBatch");
Batch<ApBatchEntry> batch = new ApBatchConverter().convertFromXML(new String(xmlDocument, StandardCharsets.UTF_8));
byte[] xml = new ApBatchConverter().convertToXML(batch, true);
String xmlString = new String(xml, StandardCharsets.UTF_8);
System.out.println(xmlString);
errors = client.validateApBatch(batch);
if (!errors.isEmpty()) {
throw new BatchValidationException(errors);
}
return;
} catch (BatchConverterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
System.out.println("ApVendorBatch");
Batch<ApWithVendorBatchEntry> batch = new ApWithVendorBatchConverter().convertFromXML(new String(xmlDocument, StandardCharsets.UTF_8));
byte[] xml = new ApWithVendorBatchConverter().convertToXML(batch, true);
String xmlString = new String(xml, StandardCharsets.UTF_8);
System.out.println(xmlString);
errors = client.validateApWithVendorBatch(batch);
if (!errors.isEmpty()) {
throw new BatchValidationException(errors);
}
return;
} catch (BatchConverterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
You can always iterate over the nodes in the XML and base decision on the fact that specific Node is missing (or is present - or has specific value) (see DocumentBuilder and Document class)
Using exceptions for decision-making or flow-control in 99% situations is considered bad practice.
Try converting the XML string to an XML tree object first and use XPath to decide if it's an ApWithVendor structure. I.e. check if there is an element like "/application/foo/vendor" path in the structure.
Once you have decided, convert the XML tree object to an object.
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.
This is driving me crazy. I have used this bit of code for lots of different projects but this is the first time it's given me this type of error. This is the whole XML file:
<layers>
<layer name="Layer 1" h="400" w="272" z="0" y="98" x="268"/>
<layer name="Layer 0" h="355" w="600" z="0" y="287" x="631"/>
</layers>
Here is the operative bit of code in my homebrew Xml class which uses the DocumentBuilderFactory to parse the Xml fed into it:
public static Xml parse(String xmlString)
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = null;
//System.out.print(xmlString);
try
{
doc = dbf.newDocumentBuilder().parse(
new InputSource(new StringReader(xmlString)));
// Get root element...
Node rootNode = (Element) doc.getDocumentElement();
return getXmlFromNode(rootNode);
} catch (ParserConfigurationException e)
{
System.out.println("ParserConfigurationException in Xml.parse");
e.printStackTrace();
} catch (SAXException e)
{
System.out.println("SAXException in Xml.parse ");
e.printStackTrace();
} catch (IOException e)
{
System.out.println("IOException in Xml.parse");
e.printStackTrace();
}
return null;
}
The context that I am using it is: school project to produce a Photoshop type image manipulation application. The file is being saved with the layers as .png and this xml file for the position, etc. of the layers in a .zip file. I don't know if the zipping is adding some mysterious extra characters or not.
I appreciate your feedback.
If you look at that file in an editor, you'll see content (perhaps whitespace) following the end element e.g.
</layers> <-- after here
It's worth dumping this out using a tool that will highlight whitespace chars e.g.
$ cat -v -e my.xml
will dump 'unprintable' characters.
Hopefully this can be helpful to someone at some point. The fix that worked was just to use lastIndexOf() with substring. Here's the code in situ:
public void loadFile(File m_imageFile)
{
try
{
ZipFile zipFile = new ZipFile(m_imageFile);
ZipEntry xmlZipFile = zipFile.getEntry("xml");
byte[] buffer = new byte[10000];
zipFile.getInputStream(xmlZipFile).read(buffer);
String xmlString = new String(buffer);
Xml xmlRoot = Xml.parse(xmlString.substring(0, xmlString.lastIndexOf('>')+1));
for(List<Xml> iter = xmlRoot.getNestedXml(); iter != null; iter = iter.next())
{
String layerName = iter.element().getAttributes().getValueByName("name");
m_view.getCanvasPanel().getLayers().add(
new Layer(ImageIO.read(zipFile.getInputStream(zipFile.getEntry(layerName))),
Integer.valueOf(iter.element().getAttributes().getValueByName("x")),
Integer.valueOf(iter.element().getAttributes().getValueByName("y")),
Integer.valueOf(iter.element().getAttributes().getValueByName("w")),
Integer.valueOf(iter.element().getAttributes().getValueByName("h")),
Integer.valueOf(iter.element().getAttributes().getValueByName("z")),
iter.element().getAttributes().getValueByName("name"))
);
}
zipFile.close();
} catch (FileNotFoundException e)
{
System.out.println("FileNotFoundException in MainController.loadFile()");
e.printStackTrace();
} catch (IOException e)
{
System.out.println("IOException in MainController.loadFile()");
e.printStackTrace();
}
}
Thanks for all the people that contributed. I suspect the error was either introduced by the zip process or by using the byte[] buffer. Any further feedback is appreciated.
I had some extra char at the end of XML, check the XML properly or do an online format of XML , which will throw error if XML is not proper. I used
Online XML Formatter
I just had this error in Sterling Integrator - when I looked at the file in a hex editor
it had about 5 extra lines of char(0), not space. No idea where they from, but this was precisely the issue, especially as I was basically doing a unity transform, so the xsl engine - in this case xalan - was obviously passing it through into the result. Removed extra rows after last close angle bracket, problem solved.
this is the way i made an XML file to a Java object(s).
i used "xjc" and a valid XML schema, and i got back some "generated" *.java files.
i imported them into a different package in eclipse.
I am reading the XML file in 2 way now.
1) Loading the XML file:
System.out.println("Using FILE approach:");
File f = new File ("C:\\test_XML_files\\complex.apx");
JAXBElement felement = (JAXBElement) u.unmarshal(f);
MyObject fmainMyObject = (MyObject) felement.getValue ();
2) Using a DOM buider:
System.out.println("Using DOM BUILDER Approach:");
JAXBElement element = (JAXBElement) u.unmarshal(test());;
MyObject mainMyObject = (MyObject ) element.getValue ();
now in method "test()" the code below is included:
public static Node test(){
Document document = parseXmlDom();
return document.getFirstChild();
}
private static Document parseXmlDom() {
Document document = null;
try {
// getting the default implementation of DOM builder
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// parsing the XML file
document = builder.parse(new File("C:\\test_XML_files\\MyXML_FILE.apx"));
} catch (Exception e) {
// catching all exceptions
System.out.println();
System.out.println(e.toString());
}
return document;
}
is this the standard way of doing XML to an Java Object?
I tested if I could access the object and everything works fine. (so far)
Do you suggest a different approach?? or is this one sufficient?
I don't know about a "standard way", but either way looks OK to me. The first way looks simpler ( less code ) so that's the way I'd probably do it, unless there were other factors / requirements.
FWIW, I'd expect that the unmarshal(File) method was implemented to do pretty much what you are doing in your second approach.
However, it is really up to you (and your co-workers) to make judgments about what is "sufficient" for your project.
I have this function:
private Node getDOM(String str) throws SearchEngineException {
DOMResult result = new DOMResult();
try {
XMLReader reader = new Parser();
reader.setFeature(Parser.namespacesFeature, false);
reader.setFeature(Parser.namespacePrefixesFeature, false);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new SAXSource(reader,new InputSource(new StringReader(str))), result);
} catch (Exception ex) {
throw new SearchEngineException("NukatSearchEngine.getDom: " + ex.getMessage());
}
return result.getNode();
}
It takes a String that contains the html document sent by the http server after a POST request, but fails to parse it properly - I only get like four nodes from the entire document. The string itself looks fine - if I print it out and copypasta it into a text document I see the page I expected.
When I use an overloaded version of the above method:
private Node getDOM(URL url) throws SearchEngineException {
DOMResult result = new DOMResult();
try {
XMLReader reader = new Parser();
reader.setFeature(Parser.namespacesFeature, false);
reader.setFeature(Parser.namespacePrefixesFeature, false);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(new SAXSource(reader, new InputSource(url.openStream())), result);
} catch (Exception ex) {
throw new SearchEngineException("NukatSearchEngine.getDom: " + ex.getMessage());
}
return result.getNode();
}
then everything works just fine - I get a proper DOM tree, but I need to somehow retrieve the POST answer from server.
Storing the string in a file and reading it back does not work - still getting the same results.
What could be the problem?
Is it maybe a problem with the xml encoding?
This seems like an encoding problem. In the code example of yours that doesn't work you're passing the url as a string into the constructor, which uses it as the systemId, and you get problems with Tagsoup parsing the html. In the example that works you're passing the stream in to the InputSource constructor. The difference is that when you pass in the stream then the SAX implementation can figure out the encoding from the stream.
If you want to test this you could try these steps:
Stream the html you're parsing through a java.io.InputStreamReader and call getEncoding on it to see what encoding it detects.
In your first example code, call setEncoding on the InputSource passing in the encoding that the inputStreamReader reported.
See if the first example, changed to explicitly set the encoding, parses the html correctly.
There's a discussion of this toward the end of an article on using the SAX InputSource.
To get a POST response you first need to do a POST request, new InputSource(url.openStream()) probably opens a connection and reads the response from a GET request. Check out Sending a POST Request Using a URL.
Other possibilities that might be interesting to check out for doing POST requests and getting the response:
Jersey Web Client
HtmlUnit