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"
.