Bug #5886
closedSerializer of s9api in Saxon 11 for json and adaptive output method seems to close underlying stream while that doesn't happen for output method xml
100%
Description
Based on my findings in https://saxonica.plan.io/boards/3/topics/9295 and what I fill explain further it appears Saxon 11 has a bug where the use of an s9api Serializer
over an OutputStream
as the Destination
of an Xslt30Transformer
's callTemplate
or applyTemplates
closes the underlying stream for output methods json and adaptive while that doesn't happen (as I think it is supposed to do) for output method xml.
So for some sample code like
package org.example;
import net.sf.saxon.s9api.*;
import javax.xml.transform.stream.StreamSource;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
public class Main {
public static void main(String[] args) throws IOException, SaxonApiException {
Processor processor = new Processor(false);
String xsltXmlOutput = "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n" +
" version=\"3.0\"\n" +
" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n" +
" exclude-result-prefixes=\"#all\"\n" +
" expand-text=\"yes\">\n" +
"\n" +
" <xsl:output method=\"xml\" indent=\"yes\"/>\n" +
"\n" +
" <xsl:template match=\"/\" name=\"xsl:initial-template\">\n" +
" <test>Run at {current-dateTime()} with {system-property('xsl:product-name')} {system-property('xsl:product-version')}</test>\n" +
" <xsl:comment>Run at {current-dateTime()} with {system-property('xsl:product-name')} {system-property('xsl:product-version')}</xsl:comment>\n" +
" </xsl:template>\n" +
" \n" +
"</xsl:stylesheet>";
XsltCompiler xsltCompiler = processor.newXsltCompiler();
XsltExecutable xsltExecutable = xsltCompiler.compile(new StreamSource(new StringReader(xsltXmlOutput)));
Xslt30Transformer xslt30Transformer = xsltExecutable.load30();
FileOutputStream stream = new FileOutputStream("xml-fragment-result.xml");
xslt30Transformer.callTemplate(null, processor.newSerializer(stream));
xslt30Transformer = xsltExecutable.load30();
xslt30Transformer.callTemplate(null, processor.newSerializer(stream));
stream.close();
String xsltJsonOutput = "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n" +
" version=\"3.0\"\n" +
" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n" +
" exclude-result-prefixes=\"#all\"\n" +
" expand-text=\"yes\">\n" +
" \n" +
" <xsl:output method=\"json\" indent=\"yes\"/>\n" +
"\n" +
" <xsl:template match=\"/\" name=\"xsl:initial-template\">\n" +
" <xsl:sequence\n" +
" select=\"map { 'tested-at' : current-dateTime(), 'with' : system-property('xsl:product-name') || ' ' || system-property('xsl:product-version') }\"/>\n" +
" </xsl:template>\n" +
" \n" +
"</xsl:stylesheet>";
xsltCompiler = processor.newXsltCompiler();
xsltExecutable = xsltCompiler.compile(new StreamSource(new StringReader(xsltJsonOutput)));
xslt30Transformer = xsltExecutable.load30();
stream = new FileOutputStream("JsonResults.json");
xslt30Transformer.callTemplate(null, processor.newSerializer(stream));
xslt30Transformer = xsltExecutable.load30();
xslt30Transformer.callTemplate(null, processor.newSerializer(stream));
stream.close();
String xsltAdaptiveOutput = "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n" +
" version=\"3.0\"\n" +
" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n" +
" exclude-result-prefixes=\"#all\"\n" +
" expand-text=\"yes\">\n" +
" \n" +
" <xsl:output method=\"adaptive\" indent=\"yes\"/>\n" +
"\n" +
" <xsl:template match=\"/\" name=\"xsl:initial-template\">\n" +
" <xsl:sequence\n" +
" select=\"map { 'tested-at' : current-dateTime(), 'with' : system-property('xsl:product-name') || ' ' || system-property('xsl:product-version') }\"/>\n" +
" </xsl:template>\n" +
" \n" +
"</xsl:stylesheet>";
xsltExecutable = xsltCompiler.compile(new StreamSource(new StringReader(xsltAdaptiveOutput)));
xslt30Transformer = xsltExecutable.load30();
stream = new FileOutputStream("AdaptiveResults.txt");
xslt30Transformer.callTemplate(null, processor.newSerializer(stream));
xslt30Transformer = xsltExecutable.load30();
xslt30Transformer.callTemplate(null, processor.newSerializer(stream));
stream.close();
}
}
the XML result file xml-fragment-result.xml
has the output of the two transformations e.g.
<?xml version="1.0" encoding="UTF-8"?>
<test>Run at 2023-02-22T14:18:42.377+01:00 with SAXON HE 11.5</test>
<!--Run at 2023-02-22T14:18:42.377+01:00 with SAXON HE 11.5-->
<?xml version="1.0" encoding="UTF-8"?>
<test>Run at 2023-02-22T14:18:42.379+01:00 with SAXON HE 11.5</test>
<!--Run at 2023-02-22T14:18:42.379+01:00 with SAXON HE 11.5-->
while for the JsonResults.json
and the AdaptiveResults.txt
the output only contains the result of the first transformation e.g.
{ "with":"SAXON HE 11.5", "tested-at":"2023-02-22T14:18:42.394+01:00" }
and e.g.
map{"with":"SAXON HE 11.5","tested-at":xs:dateTime("2023-02-22T14:18:42.401+01:00")}
And for the adaptive sample the problem also manifests itself as an error
Error
java.io.IOException: Stream Closed: Stream Closed
Exception in thread "main" net.sf.saxon.s9api.SaxonApiException: java.io.IOException: Stream Closed
at net.sf.saxon.s9api.Xslt30Transformer.callTemplate(Xslt30Transformer.java:493)
at org.example.Main.main(Main.java:101)
Caused by: net.sf.saxon.trans.XPathException: java.io.IOException: Stream Closed
at net.sf.saxon.serialize.AdaptiveEmitter.close(AdaptiveEmitter.java:332)
at net.sf.saxon.event.ProxyReceiver.close(ProxyReceiver.java:104)
at net.sf.saxon.event.CloseNotifier.close(CloseNotifier.java:36)
at net.sf.saxon.event.ComplexContentOutputter.close(ComplexContentOutputter.java:739)
at net.sf.saxon.trans.XsltController.callTemplate(XsltController.java:879)
at net.sf.saxon.s9api.Xslt30Transformer.callTemplate(Xslt30Transformer.java:485)
... 1 more
Caused by: java.io.IOException: Stream Closed
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:326)
at net.sf.saxon.serialize.UTF8Writer._flushBuffer(UTF8Writer.java:577)
at net.sf.saxon.serialize.UTF8Writer.close(UTF8Writer.java:96)
at net.sf.saxon.serialize.AdaptiveEmitter.close(AdaptiveEmitter.java:330)
... 6 more
The problem is particularly crippling if you use a Serializer
over System.out
for some transformation output as at that point it seems after the first transformation to System.out with json output method any other attempt to write/print to it is swallowed, as occurs in https://github.com/martin-honnen/SaxonJson2JsonExample1.
The problem doesn't occur with Saxon 12.
Please register to edit this issue