S9API interface

A new method setResultDocumentHandler() has been added to XsltTransformer and Xslt30Transformer, to control the destination of the output of xsl:result-document instructions. The argument is a function that is supplied with a URI, and returns a Destination object. If the application needs to be notified when writing the Destination completes, it can register a callback using Destination.onClose().

At the Controller level, a new interface ResultDocumentResolver supersedes the old OutputURIResolver (which is still supported for the time being). This change was necessary to support the full flexibility of result document handling in XSLT 3.0, for example the ability to deliver raw output, to control sequence normalization, and the item-separator property.

In previous releases an xsl:result-document instruction with no href attribute, or with href="", was treated as writing to the destination supplied (at API level) for the principal stylesheet output. From 9.9 it is treated in the same way as <xsl:result-document href="{$base-output-uri}"/> (where $base-output-uri is the initial value of current-output-uri()): this is much closer to the intent of the W3C language specification. The practical difference is that the destination for such a result document is now under the control of the ResultDocumentResolver (or OutputURIResolver) and need not be the same as the principal output destination supplied when invoking the transformation.

A new subclass of Destination has been defined, called RawDestination: this forces buildTree=no so it captures the raw output of a query or stylesheet (or of xsl:result-document) as an XdmValue.

The Destination interface has been extended with methods onClose() and closeAndNotify(); user-written implementations of Destination will need to implement these methods. This can be done easily by inheriting from the new AbstractDestination class, or if that is not possible, by delegating to DestinationHelper.

Because destinations that do sequence normalization (including for example XdmDestination, SAXDestination, and DOMDestination) use the value of the item-separator property, Saxon now makes the serialization parameters available to the Destination.getReceiver() method in all cases, no longer treating the Serializer destination as a special case.

In previous releases, the value of xsl:result-document/@href would be interpreted relative to the current working directory if no base output URI is available. This has been dropped as too dangerous (there is a tendency for result documents to be written where no-one can find them). Instead, if @href is a relative URI and no base output URI is available, an error is reported. However, when running from the command line, the value of the -o option is still interpreted as relative to the current working directory.

When an XdmDestination is supplied for the result of a query, the raw results of the query are now wrapped into a document node according to the rules of "sequence normalization" in the Serialization specification. Previously an error was reported if the result was not a single node.

The SchemaValidator class now has an asSource() method. This allows a post-validation document to be supplied as input to any (s9api or JAXP) method expecting a Source as input. For example, it can be used to supply schema-validated streamed input to a transformation, or it can be used as the return value from a URIResolver to indicate that the result of a particular call to doc() or document() should be schema-validated.

The Xslt30Transformer class now has an asDocumentDestination() method allowing the transformation to be used as a destination of other operations, for example a validation or another transformation. (The XsltTransformer class implements the Destination interface, providing a very convenient way of creating a pipeline of transformations. The ability to construct a Destination from a Xslt30Transformer provides the same capability for transformations that make full use of XSLT 3.0 capabilities.) Pipelines constructed in this way can be either streamed or unstreamed.

The method WhitespaceStrippingPolicy.makeCustomPolicy() has changed to use the standard Java 8 Predicate class in place of the Saxon version.

The classes XPathCompiler, XQueryCompiler, and XsltCompiler now have a method setFastCompilation(true) which causes the compiler to reduce the amount of time it spends optimizing the code for run-time execution speed; it may also reduce the effort devoted to ensuring good error diagnostics. This option is appropriate when compiling code that will only execute once, with a modest amount of input data, and where the code is known to be functionally correct.

A new convenience method XdmNode.getProcessor() is provided to allow the Processor associated with a given XdmNode to be retrieved (for example, for the purpose of obtaining a Serializer or XPathCompiler).

Navigating XML Documents Using s9api

Saxon 9.9 introduces a major extension of the s9api interface to take advantage of the Streams functionality in Java 8. Whereas s9api has traditionally focused on mechanisms enabling Java applications to invoke XPath, XSLT, XQuery, and XSD processing, the new extensions are designed to make it easier to manipulate the inputs and outputs of these processors directly in the Java code. The starting point is that the results are generally delivered either in the form of XdmValue objects or as Iterator<XdmItem> objects.

The interface net.sf.saxon.s9api.streams.Step represents a function from an item to a stream of items. For example, child() (a static method defined in class net.sf.saxon.s9api.streams.Steps) is a Step that delivers the children of a node. Given a node N and a step S, N.select(S) returns a stream of items; so N.select(child()) returns a stream containing the children of the node.

Combining a step with a predicate gives a new step: for example child.where(isElement()) is a step that selects the element children of a node; so N.select(child().where(isElement())) returns the element children of N as a stream. The static method isElement() used here returns a predicate defined in the utility class net.sf.saxon.s9api.streams.Predicates, but of course any Predicate can be used in its place.

Commonly-used filters can be expressed in abbreviated form. For example the first title element in a book can be obtained by writing book.select(descendant("title").first()).

Steps can be composed using the then() method, so N.select(child().then(child())) selects all the grandchildren of a node.

The resulting stream of nodes can be processed using standard Java 8 techniques, for example it can be collected into a list: N.select(child().then(atomize())).collect(Collectors.toList()) atomizes the values of the child nodes and returns the result as a Java List whose members are XdmAtomicValue instances.

The class XdmValue has a constructor accepting a Stream, so for example new XdmValue(N.select(child())) delivers an XdmValue containing the children of a supplied node N.

The Java expressions that can be assembled using these primitives are very close to XPath in their expressive power. Although Saxon will not be able to optimize them in the same way as it can optimize XPath expressions (for example, a filter will always literally work by testing each item in a sequence to see if it matches), there is a considerable performance benefit over XPath in that the Java compiler does all the hard work of parsing; it will also detect many compile-time errors that would not be detected until execution time with XPath.

In addition there are some new convenience methods such as XdmNode.attribute("xxx") which gets the value of an attribute given only its local name, XdmNode.children() which gets all the children of a node, and XdmNode.children("xxx") which gets all the child elements with local name "xxx".