XSLT transformations from C#
You can perform a transformation using the C# Saxon.Api
interface as follows
(the same classes can also be used, of course, from other languages supported on .NET,
but the description here is oriented to C#):
-
Create a Processor and set any global configuration options on the
var processor = new Processor();Processor
. It is recommended to create a singleProcessor
for the entire application. -
Call
var compiler = processor.NewXsltCompiler();NewXsltCompiler()
to create an XsltCompiler object, and set any options that are local to a specific compilation (for example, the destination of error messages). -
Decide how you want to handle compile-time errors. Some options are:
If your application will only be used to execute stylesheets that have been thoroughly tested elsewhere (for example, in a development environment such as Oxygen), then you can simply rely on the exception thrown by the compiler if an error occurs. However, the exception won't contain detailed diagnostics to say what is wrong.
If your application runs from the command line, then in normal circumstances any error messages will be written to the console, and no special action is needed.
If however the application runs in a web server, or within a GUI framework, then you will need to think about where to direct the error messages. For example, you could send them to a log file, or you could display them on screen. Either way, you will want to register an ErrorReporter that deals with the messages. For example, you could capture the messages in a list (for later analysis) like this:
compiler.ErrorReporter = err => errorList.Add(err)Or you could send them to a log file like this:
compiler.ErrorReporter = err => Console.Error.WriteLine(err.Message)The error object that is passed to the
ErrorReporter
delegate is not an exception (it cannot be thrown). It is an object of class Error, and its main properties are:Message
: an error messageErrorCode
: the error codeLocation
: the location in the stylesheet where the error was detectedIsWarning
: distinguishes warnings from fatal error conditions
-
Call the
var executable = compiler.Compile(...)Compile()
method to compile a stylesheet. The result is an XsltExecutable, which can be used as often as you like, in the same thread or in different threads.This method has a number of overloads, allowing the stylesheet to be supplied in different ways. It can be supplied as a URI, as a
Stream
, as aTextReader
, or as anXmlReader
.Except for trivial stylesheets containing a single module, it's important that the stylesheet should have a known base URI. The base URI is used at compile time for resolving any relative URI reference appearing in an
xsl:include
orxsl:import
declaration; it's also used at run-time when calling functions such asdocument()
,unparsed-text()
, orjson-doc()
. You can supply a base URI either by setting theBaseUri
property on theXsltCompiler
, or as an (implicit or explicit) parameter on the call to theCompile
method.Compiling a stylesheet can often be an expensive process in comparison with the actual execution (especially when the stylesheet is large and the source document is small). It's therefore desirable to reuse the
XsltExecutable
for multiple transformations where possible. Once created, theXsltExecutable
is read-only and thread-safe. -
The next step is to create an XsltTransformer, or Xslt30Transformer object, by calling the
var transformer = executable.Load(); var transformer = executable.Load30();Load()
orLoad30()
method on theXsltExecutable
.Either of these can be used to run the transformation (regardless of which XSLT version you are using), but they have different capabilities:
-
The XsltTransformer is geared towards the traditional way of running an XSLT transformation, by supplying a principal source document as input. When you call the
Run()
method, the template rule that best matches this source document is located and executed.The input document is supplied as a
Stream
(containing lexical XML), using the methodSetInputStream
. This method also expects a URI, which is used as the base URI of the source document in case it contains relative references to other documents.The destination for the result is supplied as the argument to the
Run()
method. This can be any implementation of the interface IDestination; the most common destinations are Serializer (which causes the transformation output to be serialized, typically as XML or HTML), and XdmDestination which delivers the result as an XDM node tree. (Read thexdmDestination.XdmNode
property on completion to access the root of the tree.)Options for serializing the result can be set either on the
Serializer
object, or using<xsl:output>
declarations in the stylesheet. Options set via the API take precedence.Another option is to send the result of a transformation to another
XsltTransformer
. The classXsltTransformer
implements theIDestination
interface, so the output of one transformation can form the input to another, making it easy to construct a pipeline of transformations.The
XsltTransformer
also allows you to start executing the stylesheet with a named template as the entry point. In this case there will not necessarily be a source document, though you can still supply one, and it will be used as the context item for global variables.You can use methods on the
XsltTransformer
to set values for global stylesheet parameters, but not for parameters declared at the level of a particularxsl:template
element.Typical usage:
var transformer = executable.Load(); transformer.SetInputStream(...); transformer.SetParameter(new QName("debug"), new XdmAtomicValue(true)); var writer = new StringWriter(); var serializer = processor.NewSerializer(writer); serializer.SetOutputProperty(Serializer.INDENT, "yes"); transformer.Run(serializer); Console.WriteLine(writer.ToString()); -
The Xslt30Transformer class was a later introduction, and as its name suggests, it provides new ways of executing stylesheet code that are defined in the XSLT 3.0 specification, though Saxon also allows you to use the same entry points with 1.0 or 2.0 stylesheets. Among the new capabilities are:
- You can invoke a specific stylesheet function, with parameters, rather than invoking a named template or template rule.
- If you start execution with a named template or template rule, you can supply values for the parameters defined on that template, as well as the global stylesheet parameters.
- Whether you execute a template or a function, you can return the results in raw form rather than wrapping them in a result tree. For example, a function (or template) might return a sequence of strings, or a single boolean, or a map, or even a function.
- There is no longer any necessary relationship between the "principal source document" (if it still exists) and the context item for evaluating global variables. The two things are quite independent of each other.
It is still possible to wrap the output in a result tree and send it to a IDestination (which might be a Serializer), but this is no longer mandatory.
The
Xslt30Transformer
does not implement theIDestination
interface so it is not quite as convenient asXsltTransformer
when constructing a pipeline.
Both
XsltTransformer
andXslt30Transformer
can be serially reused, but they must not be shared across multiple threads. Both allow you to set any options required for the specific transformation: for example, the global context item, the stylesheet parameters, and callbacks for resolving URIs used in functions such asdoc()
,unparsed-text()
, andjson-doc()
. -
Examples of Saxon.Api transformations are included in the saxon-resources
download file, see the sample application Examples.cs, and API examples for .NET for more information.
For instance, XsltSimple2
in Examples.cs
runs as follows: