Bug #3120

Streaming not working from JAXP Transformation API

Added by Alex Jitianu over 3 years ago. Updated about 3 years ago.

Start date:
Due date:
% Done:


Estimated time:
Legacy ID:
Applies to branch:
Fix Committed on Branch:
9.7, trunk
Fixed in Maintenance Release:



I'm trying to use Saxon through its API to execute a "streaming" transformation, like in this example:


import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXSource;

import net.sf.saxon.TransformerFactoryImpl;

import org.xml.sax.InputSource;

public class Streaming {

  private static String xsl = 
      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
      "<xsl:stylesheet xmlns:xsl=\"\"\n" + 
      "    xmlns:xs=\"\"\n" + 
      "    exclude-result-prefixes=\"xs\"\n" + 
      "    version=\"3.0\">\n"
      + "<xsl:mode streamable=\"true\"/>\n" + 
      "    <xsl:template match=\"*:person\"><xsl:value-of select=\"@id\"/></xsl:template>\n" + 
  private static String xml = "<person id='test'/>";

  public static void main(String[] args) throws TransformerException {
    TransformerFactory factory = TransformerFactoryImpl.newInstance();
    Transformer transformer = factory.newTransformer(new StreamSource(new StringReader(xsl)));
    InputSource inputSource = new InputSource(new StringReader(xml));
    SAXSource xmlSource = new SAXSource(inputSource);
    StringWriter writer = new StringWriter();
    transformer.transform(xmlSource, new StreamResult(writer));

The following warning appears in the console:

  Requested initial mode  is streamable, but the supplied input is not streamed

which is a hint that the transformation is not ran in "streaming mode". From what I can tell something strange occurs inside net.sf.saxon.s9api.XsltTransformer.transform() :

               if (initialSource instanceof NodeInfo) {
                } else if (initialSource instanceof DOMSource) {
                    NodeInfo node = controller.prepareInputTree(initialSource);
                    initialSource = node;
                } else {
                    boolean close = (initialSource instanceof AugmentedSource && ((AugmentedSource)initialSource).isPleaseCloseAfterUse());
                    NodeInfo node = controller.makeSourceTree(initialSource, close, getSchemaValidationMode().getNumber());
                    initialSource = node;

When a SAXSource passes through this code it will be transformed into a NodeInfo and because of that, inside net.sf.saxon.Controller.transform(Source, Receiver) a decision is taken to go into net.sf.saxon.Controller.transformDocument() where that warning is generated. Thank you for taking the time to look into this.



#1 Updated by Michael Kay over 3 years ago

Thanks for reporting it.

I would recommend using the Xslt30Transformer class (rather than XsltTransformer) for streaming applications, but I'll see if I can find a way to get this case to work. The challenge is that XsltTransformer uses the xmlSource argument to transform() to set both the global context item and the initial match selection; in streamed mode we would have to set the global context item to "absent" since that cannot be a streamed node.

#2 Updated by Alex Jitianu over 3 years ago

Thank you for trying to fix it! A code like this is used inside Oxygen XML Editor. I assume we could detect the stylesheet version and instantiate an Xslt30Transformer but, to be honest, that would complicate things quite a bit. Currently most of our code works with Saxon though its JAXP implementations (for the XSLT transformation part).

#3 Updated by Michael Kay over 3 years ago

I've been experimenting with ways of meeting this requirement but I'm not happy with the results.

Firstly, the JAXP transform() semantics say that the source document supplied in the first argument becomes the global context item for the transformation. We can't do that and support streaming at the same time, and it's not easy to identify conditions where we should ignore the requirement. We could say that we base it on whether the initial mode is streamable, but the problem is that we make a lot of decisions before we know the initial mode.

Secondly, there are a lot of other features in 3.0 where the JAXP API simply can't be stretched to accommodate them. Static parameters, initial function, supplying parameters to the initial function or template, returning "raw" results (rather than a result tree) etc etc. So I don't think that making a heroic effort to support streaming via the JAXP interface, when we can't support all these other 3.0 features, is really a good strategy. I think we have to treat the JAXP API as legacy.

#4 Updated by Michael Kay over 3 years ago

Incidentally, although the name Xslt30Transformer might suggest that it's only suitable for running XSLT 3.0, that isn't the case - although it's an extended interface designed to accommodate the needs of XSLT 3.0, it can also do everything that 1.0 and 2.0 did. So you could use it for everything.

#5 Updated by Michael Kay over 3 years ago

  • Subject changed from Streaming not working from API to Streaming not working from JAXP Transformation API

#6 Updated by Radu Coravu over 3 years ago

We are due to release Oxygen 19 in about a month. Can the JAXP implementatin in Saxon be made to work with streaming until then? Otherwise we'll probably get quite a few of end users complaining about streaming no longer working with Oxygen...

If we were to handle this on our side, what would be the equivalent Java code that we would need to call instead of the JAXP one?

#7 Updated by Michael Kay over 3 years ago

I think we could possibly do this:

(a) have JAXP's newTransformer() create a s9api Xslt30Transformer rather than an XsltTransformer

(b) if the initial mode is declared as streamable, and if the Source is a StreamSource or SAXSource, then DON'T set the global context item.

I think this could be a rather disruptive change, so the safest way to do it is probably to create a StreamingTransformerFactory separate from the existing EnterpriseTransformerFactory and leave the existing one unchanged.

#8 Updated by Alex Jitianu over 3 years ago

A StreamingTransformerFactory could be a solution. We would probably publish a new transformer type "Saxon EE with Streaming" and the user will have to explicitly select it for a transformation scenario in order for streaming to activate.

We plan to release Oxygen version 19.0 in about a month so we still have one or two weeks of development remaining. Do you think it would be possible to release a new Saxon version with this fix before this time runs up?

Thank you!

#9 Updated by Michael Kay over 3 years ago

  • Status changed from New to Resolved
  • Priority changed from Low to Normal
  • Applies to branch 9.7, 9.8 added
  • Fix Committed on Branch 9.7, 9.8 added

A StreamingTransformerFactory has been implemented as suggested. We will work out offline how to get this to Syncrosoft for testing.

#10 Updated by O'Neil Delpratt over 3 years ago

  • Status changed from Resolved to Closed
  • % Done changed from 0 to 100
  • Fixed in Maintenance Release added

Bug fix applied in the Saxon maintenance release.

#11 Updated by O'Neil Delpratt about 3 years ago

  • Fix Committed on Branch trunk added
  • Fix Committed on Branch deleted (9.8)

#12 Updated by O'Neil Delpratt about 3 years ago

  • Applies to branch deleted (9.8)

Please register to edit this issue

Also available in: Atom PDF