XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition (797 page)

BOOK: XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition
6.2Mb size Format: txt, pdf, ePub
  • If you don't mind your code having a compile-time reference to Saxon classes, the simplest, fastest, and most robust approach is to replace the call:

factory = TransformerFactory.newInstance();

by:

factory = new net.sf.saxon.TransformerFactoryImpl();

  • You can choose Saxon by setting the Java system property named
    javax.xml.transform.TransformerFactory
    to the value
    net.sf.saxon.TransformerFactoryImpl
    . Use the
    -D
    option on the Java command when you invoke your application. Note that this goes before the name of the class to be executed:

java -Djavax.xml.transform.TransformerFactory=

         net.sf.saxon.TransformerFactoryImpl com.my-com.appl.Program

This all goes on one line. In practice of course you won't want to type this more than once, so create a batch file or shell script using your text editor, and invoke this instead.

  • Create a file called
    jaxp.properties
    within the directory
    $JAVA_HOME/lib
    (where
    $JAVA_HOME
    is the directory containing your Java installation), and include in this file a line of the form
    key=value
    , where
    key
    is the property key
    javax.xml.transform .TransformerFactory
    and
    value
    is the Saxon class
    net.sf.saxon .TransformerFactoryImpl
    .
  • Put the call:

   System.setProperty (“javax.xml.transform.TransformerFactory”,

                       “net.sf.saxon.TransformerFactoryImpl”)

in your application, to be executed at runtime. This technique is useful if you want to run several different JAXP processors from the same application, perhaps in order to compare their results or to benchmark their performance.

  • In Java 6 you can call:

TransformerFactory.newInstance(“net.sf.saxon.TransformerFactoryImpl”, null)

If you are still having trouble, try setting the Java system property
jaxp.debug
to
1
. This gives diagnostic information about the loading process.

The
TransformerFactory
interface in JAXP has a method
setAttribute()
, which allows processor-dependent configuration options to be set. Saxon offers a large number of such options, corresponding to all the options on the command line and a few more. Details are in the Saxon Javadoc documentation at
http://www.saxonica.com/documentation/javadoc/index.html
. The names of the attributes and a description of their purpose can be found in the specification of the class
net.sf.saxon.FeatureKeys
.

As well as accepting the standard three kinds of
Source
object defined in JAXP, that is,
StreamSource
,
SAXSource
, and
DOMSource
, a Saxon transformation can also take other kinds of input. Saxon's tree model uses an interface
net.sf.saxon.om.NodeInfo
to represent a node, and because this interface extends the JAXP
Source
interface, any
NodeInfo
can be used as the source of a transformation. If you want to use a document as input to several transformations, you can build the tree once using the factory method
Configuration.buildDocument()
, and then supply the returned
NodeInfo
to the Transformer's
transform()
method. You can also construct a
NodeInfo
as a wrapper around a DOM, JDOM, DOM4 J, or XOM document, and supply this wrapper to the transformer in the same way. As well as supplying the primary input to the transformation, all these techniques can be used to supply additional documents as the values of stylesheet parameters (just supply a
Source
as the argument to the Transformer's
setParameter()
method), or documents read using the
doc()
or
document()
functions (you can return any kind of
Source
from the user-supplied
URIResolver
).

The Schema Validation API

Saxon-SA supports the schema validation API defined in package
javax.xml.validation
. It provides an implementation of the
SchemaFactory
interface (
com.saxonica.jaxp.SchemaValidatorImpl
), which is currently in a separate package and JAR file for the benefit of JDK 1.4 users—this part of the JAXP API appears first in JDK 1.5. This allows you to load a schema, and to validate instance documents in either streaming (SAX) mode, or while building a DOM tree. Both these options are only really useful if you want to do freestanding validation.

Because the JAXP transformation API was designed for XSLT 1.0, it doesn't provide any direct way to request validation of the source document for a transformation, so Saxon provides its own mechanism. In fact, it provides several. One approach is to set a system-wide switch indicating that all source documents should be validated strictly or laxly. This can be done by calling
setAttribute(FeatureKeys.SCHEMA_VALIDATION, Validation.STRICT)
on the
TransformerFactory
object. If you want to control validation on a per-document basis, then you can create an
AugmentedSource
object with the appropriate options set.
AugmentedSource
is a JAXP Source object, so it can be used as input to a transformation, and as the name suggests it bundles together an underlying
Source
object with a number of parameters indicating how the source should be processed—one of these being an option to request schema validation.

Using XPath Expressions in Saxon

Another JAXP component introduced in JDK 1.5 is the interface for evaluating XPath expressions. Saxon implements this one too, allowing the source object to be either a native Saxon tree, or a DOM, JDOM, XOM, or DOM4 J tree wrapped in a Saxon
NodeInfo
wrapper.

Unfortunately the JAXP XPath API suffers a number of weaknesses. It is designed for XPath 1.0 rather than 2.0, which means it cannot handle the full range of data types that XPath 2.0 supports. I would therefore be strongly inclined to recommend using Saxon's s9api interface instead, which is described in the next section. There is also a legacy API in package
net.sf.saxon.sxpath
which you can use if you need it to work on JDK 1.4. This is modeled on the JAXP API, but has many differences of detail.

The s9api Interface

Saxon's s9api interface for Java was newly introduced in Saxon 9.0, strongly influenced by the success of the API provided on the .NET platform. The design aims were:

  • To provide a consistent approach to XSLT, XQuery, XPath, and XML Schema processing
  • To avoid the clutter caused by conforming to a ragbag of legacy interfaces and by exposing implementation-level classes
  • To provide a greater level of type safety, partly achieved by exploiting new Java 5 language features such as generics

The starting point for an application is to create an instance of
Processor
(all classes are in package
net.sf.saxon.s9api
). This provides a few system-wide configuration options, acts as an owner of shared resources, and provides factory methods for other functionality.

Whether you are running XSLT, XQuery, or XPath, you follow the same sequence of steps:

1.
Create a compiler for the appropriate language, using one of the three factory methods
newXsltCompiler()
,
newXQueryCompiler()
, and
newXPathCompiler()
defined on the
Processor
class.

2.
Set properties on the compiler defining the static context for the compilation, and any other compile time options. For example, in the case of XPath this includes the base URI, the declared namespaces, and the external variables available to the XPath expression.

3.
Call the
compile()
method on the compiler. This creates a corresponding executable (
XsltExecutable
,
XQueryExecutable
, or
XPathExecutable
) which represents the compiled program or expression as an object in (potentially shared) memory. The principle in each case is that you only compile the object once, and you can run it as often as you like in the same thread or in different threads.

4.
Load the executable, by calling its
load()
method, which returns a runtime object called variously the
XsltTransformer
,
XQueryEvaluator
, or
XPathSelector
.

5.
Configure the runtime object by setting options that affect a single evaluation (that is, the dynamic evaluation context). This typically includes the node to be used as the initial context node, the runtime values of variables/parameters, and serialization options.

6.
Finally, run the transformation or query by calling one of the following methods:

  • transform()
    for XSLT
  • evaluate()
    ,
    iterator()
    , or
    run()
    for XQuery
  • evaluate()
    ,
    evaluateSingle()
    , or
    iterator()
    for XPath

The
Processor
also allows you to obtain a
DocumentBuilder
, which is used for building source documents in tree form, and a
SchemaManager
, which can be used to load schema documents into a central pool of schema definitions, which are then shared by all operations running under the control of the same
Processor
.

The s9api package includes a set of classes for representing objects defined in the XDM data model, and reflecting the XDM type hierarchy:

  • XdmValue
    (a sequence of items)
  • XdmItem
    (a node or atomic value)
  • XdmNode
    (a node)
  • XdmAtomicValue
    (an atomic value)

and these classes provide methods for converting between the XDM objects and native Java objects.

The result of a query or XPath expression can be returned as an
XdmValue
; alternatively, the relevant runtime objects implement the Java
Iterable
interface, so you can process the results like this:

XPathCompiler compiler = processor.newXPathCompiler();

XPathSelector selector = compiler.compile(“//book[price > 20]”).load();

XDMNode doc = processor.newDocumentBuilder().build(new StreamSource(“in.xml”))

selector.setContextItem(doc);

for (XdmItem item : selector) {

   System.out.println(“

  • ” + item + “
  • ”);

    }

    The result of performing a transformation (or a validation) can be sent to any kind of
    Destination
    object. This is analogous to the JAXP
    Result
    object, except that it is possible to implement your own kinds of
    Destination
    . The system allows you to choose a number of possible destinations:

    • To serialize the output, send it to a
      Serializer
      .
    • To send it to a SAX
      ContentHandler
      , choose a
      SAXDestination
      .
    • If you want the result tree as an
      XdmNode
      , choose an
      XdmDestination
      .
    • If you want a DOM document node, use a
      DOMDestination
      .
    • If you want to validate the output against a schema, send it to a
      SchemaValidator
      , which can be constructed from the
      SchemaManager
      .
    • If you want to apply a further transformation, the
      XsltTransformer
      class is also an implementation of
      XdmDestination
      .

    Other books

    Devil’s Kiss by Zoe Archer
    Angels by Marian Keyes
    Extinction by Sean Platt & Johnny B. Truant