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

BOOK: XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition
2.96Mb size Format: txt, pdf, ePub

Using Saxon from a .NET Application

There is no equivalent to the JAXP interface on .NET, so Saxon provides its own APIs. (It could have attempted to emulate the APIs in the
System.Xml.Xsl
namespace, but as these are concrete classes rather than interfaces, it would still be difficult to write applications that work interchangeably with Saxon or with the native XSLT 1.0 processor in .NET.). Applications can of course be written in any .NET language, for example C# or VB.NET.

The API follows a three-stage Compile/Load/Go model. If you read the previous section describing s9api, then this will already be familiar, because s9api was modeled on the .NET interface. The steps are as follows:

1.
Create a
Processor
object. This contains global Saxon configuration information.

2.
Create an
XsltCompiler
object. This contains compile-time options for processing a stylesheet.

3.
Compile the stylesheet to create an
XsltExecutable
object. The
XsltExecutable
represents the compiled stylesheet, and can be used as often as you like, in multiple threads concurrently.

4.
Each time you want to run a transformation, load the stylesheet from the
XsltExecutable
to create an
XsltTransformer
. The
XsltTransformer
is generally used once only. You can set parameters that affect this transformation, and then call the
Run()
method to perform the transformation.

The source document is built by creating a
DocumentBuilder
and calling its
Build()
method: the input can come from a URI, a stream, an
XmlNode
, or an
XmlReader
. It is then supplied by setting the
InitialContextNode
property of the
XsltTransformer
.

The result destination is supplied as an argument of the
Run()
method, and may be any
XmlDestination
object. One kind of destination is a
Serializer
; you can create a
Serializer
and initialize it with settings of the various output properties such as indent and encoding, which override any values supplied in the stylesheet. Alternatively, you can supply a
TextWriterDestination
which bridges to the .NET world by feeding the output to a .NET
TextWriter
, or you can supply an
XdmDestination
to get the result in the form of a Saxon tree, or a
DomDestination
to get it in the form of a
System.Xml
DOM node.

Here is an example showing a simple transformation (the full code is in
ShowAccount.cs
):

    public static void SimpleTransformation(String sourceUri, String xsltUri) {

        // Create a Processor instance

        Processor processor = new Processor();

        // Load the source document

        XdmNode input = processor.NewDocumentBuilder().Build(new Uri(sourceUri));

        // Create a transformer for the stylesheet

        XsltTransformer transformer =

            processor.NewXsltCompiler().Compile(new Uri(xsltUri)).Load();

        // Set the root node of the source document to be the initial context node

        transformer.InitialContextNode = input;

        // Set a parameter to the transformation

        transformer.SetParameter(new QName(“”, “”, “greeting”),

                                 new XdmAtomicValue(“hello”));

        // Create a serializer

        Serializer serializer = new Serializer();

        serializer.SetOutputWriter(Console.Out);

        serializer.SetOutputProperty(Serializer.INDENT, “yes”);

        // Transform the source XML to Console.Out

        transformer.Run(serializer);

    }

The API also allows you to control schema validation of source documents, and to evaluate XPath and XQuery expressions. The APIs for XPath and XQuery use the same Compile/Load/Go metaphor, and the method names are chosen to be as consistent as possible.

Saxon Tree Models

Saxon defines an internal interface, the
NodeInfo
interface, to represent the XPath data model, and the product is capable of transforming any data source that supplies an implementation of this interface. There are several implementations of this interface available:

  • The default is the Tiny Tree, which as the name implies, is optimized for space, but also turns out to be the fastest implementation under many circumstances.
  • The Linked Tree is a more conventional tree, which is sometimes faster to navigate than the Tiny Tree but takes longer to build and occupies more space. Saxon uses this model internally for stylesheets and schemas.
  • There is an implementation of
    NodeInfo
    that wraps a standard level-3 DOM.
  • There are further implementations of
    NodeInfo
    that wrap a JDOM tree, a XOM tree, or a DOM4 J tree.

If none of these is suitable, you can in principle write your own. For example, you could write an implementation of
NodeInfo
that fetches the underlying data from a relational database.

Although Saxon allows an input document to be supplied as a DOM, processing a DOM is much slower than processing Saxon's native tree implementations. Unless your input already exists in the form of a DOM, it's better to supply a
StreamSource
or
SAXSource
and let Saxon build the tree itself. If you want to build the tree yourself so that it can be used more than once, Saxon supplies a method
Configuration.buildDocument()
. The
Configuration
is a Saxon object that underpins the
TransformerFactory
, and if you are running any kind of complex workload, you will probably want to manipulate it explicitly rather than via JAXP interfaces.

In the .NET API, nodes are represented by the .NET class
XdmNode
. This is a simple wrapper around the Java
NodeInfo
object.

Extensibility

In this section, I will describe the facilities Saxon provides for user-written extension functions, and also the way that Saxon handles collations. Following this, I'll look at a few of the extension functions that come ready-supplied with the Saxon product.

For all these extensions, the namespace prefix
saxon
needs to be declared as
xmlns:saxon=“http://saxon.sf.net/”
. Don't forget the trailing
/
.

Try to avoid using extension functions that have side effects: Saxon (especially Saxon-SA) has an aggressive optimizer that will often rearrange expressions into a very different form from the way they were originally written, and this can make side effects very unpredictable. The most common trap is to call such a function in the
select
expression of

. Such a function will usually not be called until the variable is first referenced, and if the variable is never referenced, the function will probably never be called at all.

Writing Extension Functions in Java

Saxon allows you to write extension functions in Java. The facilities can be extensively customized, but the simplest approach is to use a namespace URI in the function name that reflects the name of the Java class. For example:

              select=“Date:new()”

              xmlns:Date=“java:java.util.Date”/>

This returns an XPath value that is a wrapper for a Java object of class
java.util.Date
. Saxon maps Java classes into the XPath type hierarchy (as a new kind of atomic value), so you can declare the type of this value as:

              select=“Date:new()”

              xmlns:Date=“java:java.util.Date”

              as=“class:java.util.Date”

              xmlns:class=“http://saxon.sf.net/java-type”/>

Other books

City in the Sky by Glynn Stewart
When Cicadas Cry by Laura Miller
Sweet Disgrace by Cherrie Lynn
Dastardly Bastard by Edward Lorn
Hemispheres by Stephen Baker
The Rasner Effect by Mark Rosendorf
One of Cleopatra's Nights by Théophile Gautier